Skip to content

Commit

Permalink
Match rust integer type to minimum and maximum constraints (#6985)
Browse files Browse the repository at this point in the history
  • Loading branch information
fralalonde authored and wing328 committed Dec 10, 2017
1 parent 8c92d3a commit f49109c
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
package io.swagger.codegen.languages;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import io.swagger.codegen.*;
import io.swagger.models.*;
import io.swagger.models.parameters.BodyParameter;
Expand All @@ -21,8 +14,6 @@
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -33,6 +24,8 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {

private HashMap<String, String> modelXmlNames = new HashMap<String, String>();

private static final String NO_FORMAT = "%%NO_FORMAT";

protected String apiVersion = "1.0.0";
protected String serverHost = "localhost";
protected int serverPort = 8080;
Expand Down Expand Up @@ -908,12 +901,47 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
}
}

// Handle custom unsigned integer formats.
if ("integer".equals(property.baseType)) {
// custom integer formats (legacy)
if ("uint32".equals(property.dataFormat)) {
property.datatype = "u32";
} else if ("uint64".equals(property.dataFormat)) {
property.datatype = "u64";

} else {
// match int type to schema constraints
Long inclusiveMinimum = property.minimum != null ? Long.parseLong(property.minimum): null;
if (inclusiveMinimum != null && property.exclusiveMinimum) {
inclusiveMinimum++;
}

// a signed int is required unless a minimum greater than zero is set
boolean unsigned = inclusiveMinimum != null && inclusiveMinimum >= 0;

Long inclusiveMaximum = property.maximum != null ? Long.parseLong(property.maximum): null;
if (inclusiveMaximum != null && property.exclusiveMaximum) {
inclusiveMaximum--;
}

switch (property.dataFormat == null ? NO_FORMAT : property.dataFormat) {
// standard swagger formats
case "int32":
property.datatype = unsigned ? "u32" : "i32";
break;

case "int64":
property.datatype = unsigned ? "u64" : "i64";
break;

case NO_FORMAT:
property.datatype = matchingIntType(unsigned, inclusiveMinimum, inclusiveMaximum);
break;

default:
// unknown format
LOGGER.warn("The integer format '{}' is not recognized and will be ignored.", property.dataFormat);
property.datatype = matchingIntType(unsigned, inclusiveMinimum, inclusiveMaximum);
}
}
}

Expand All @@ -924,6 +952,46 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
}
}

static long requiredBits(Long bound, boolean unsigned) {
if (bound == null) return 0;

if (unsigned) {
if (bound < 0) {
throw new RuntimeException("Unsigned bound is negative: " + bound);
}
return 65 - Long.numberOfLeadingZeros(bound >> 1);
}

return 65 - Long.numberOfLeadingZeros(
// signed bounds go from (-n) to (n - 1), i.e. i8 goes from -128 to 127
bound < 0 ? Math.abs(bound) - 1 : bound);
}

static String matchingIntType(boolean unsigned, Long inclusiveMin, Long inclusiveMax) {
long requiredMinBits = requiredBits(inclusiveMin, unsigned);
long requiredMaxBits = requiredBits(inclusiveMax, unsigned);
long requiredBits = Math.max(requiredMinBits, requiredMaxBits);

if (requiredMaxBits == 0 && requiredMinBits <= 16) {
// rust 'size' types are arch-specific and thus somewhat loose
// so they are used when no format or maximum are specified
// and as long as minimum stays within plausible smallest ptr size (16 bits)
// this way all rust types are obtainable without defining custom formats
// this behavior (default int size) could also follow a generator flag
return unsigned ? "usize" : "isize";

} else if (requiredBits <= 8) {
return unsigned ? "u8" : "i8";

} else if (requiredBits <= 16) {
return unsigned ? "u16" : "i16";

} else if (requiredBits <= 32) {
return unsigned ? "u32" : "i32";
}
return unsigned ? "u64" : "i64";
}

@Override
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
return super.postProcessModelsEnum(objs);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.swagger.codegen.languages;

import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;

public class RustServerCodegenTest {

@Test
public void testRustIntSize() {
assertEquals(RustServerCodegen.matchingIntType(true, null, null), "usize");
assertEquals(RustServerCodegen.matchingIntType(true, 0L, null), "usize");
assertEquals(RustServerCodegen.matchingIntType(true, 0xFFL, null), "usize");
assertEquals(RustServerCodegen.matchingIntType(true, 0xFFFFL, null), "usize");
assertEquals(RustServerCodegen.matchingIntType(true, 0x10000L, null), "u32");
assertEquals(RustServerCodegen.matchingIntType(true, 0xFFFFFFFFL, null), "u32");
assertEquals(RustServerCodegen.matchingIntType(true, 0x100000000L, null), "u64");
assertEquals(RustServerCodegen.matchingIntType(true, null, 0xFFL), "u8");
assertEquals(RustServerCodegen.matchingIntType(true, null, 0x100L), "u16");
assertEquals(RustServerCodegen.matchingIntType(true, null, 0xFFFFL), "u16");
assertEquals(RustServerCodegen.matchingIntType(true, null, 0x10000L), "u32");
assertEquals(RustServerCodegen.matchingIntType(true, null, 0xFFFFFFFFL), "u32");
assertEquals(RustServerCodegen.matchingIntType(true, null, 0x100000000L), "u64");
assertEquals(RustServerCodegen.matchingIntType(true, 0xFFL, 0xFFL), "u8");
assertEquals(RustServerCodegen.matchingIntType(true, 0x100L, 0x100L), "u16");
assertEquals(RustServerCodegen.matchingIntType(true, 0xFFFFL, 0xFFFFL), "u16");
assertEquals(RustServerCodegen.matchingIntType(true, 0x10000L, 0x10000L), "u32");
assertEquals(RustServerCodegen.matchingIntType(true, 0xFFFFFFFFL, 0xFFFFFFFFL), "u32");
assertEquals(RustServerCodegen.matchingIntType(true, 0x100000000L, 0x100000000L), "u64");

assertEquals(RustServerCodegen.matchingIntType(false, null, null), "isize");
assertEquals(RustServerCodegen.matchingIntType(false, -256L, null), "isize");
assertEquals(RustServerCodegen.matchingIntType(false, -257L, null), "isize");
assertEquals(RustServerCodegen.matchingIntType(false, -16385L, null), "isize");
assertEquals(RustServerCodegen.matchingIntType(false, ((long) Short.MIN_VALUE) - 1, null), "i32");
assertEquals(RustServerCodegen.matchingIntType(false, (long) Integer.MIN_VALUE, null), "i32");
assertEquals(RustServerCodegen.matchingIntType(false, ((long) Integer.MIN_VALUE) - 1, null), "i64");
assertEquals(RustServerCodegen.matchingIntType(false, Long.MIN_VALUE, null), "i64");
assertEquals(RustServerCodegen.matchingIntType(false, null, 127L), "i8");
assertEquals(RustServerCodegen.matchingIntType(false, null, 128L), "i16");
assertEquals(RustServerCodegen.matchingIntType(false, null, (long) Short.MAX_VALUE), "i16");
assertEquals(RustServerCodegen.matchingIntType(false, null, (long) Short.MAX_VALUE + 1), "i32");
assertEquals(RustServerCodegen.matchingIntType(false, null, (long) Integer.MAX_VALUE), "i32");
assertEquals(RustServerCodegen.matchingIntType(false, null, (long) Integer.MAX_VALUE + 1), "i64");
assertEquals(RustServerCodegen.matchingIntType(false, null, Long.MAX_VALUE), "i64");
}

}

0 comments on commit f49109c

Please sign in to comment.