feat(rust): allow more granular Rust integer types and cleaned up clippy warnings (#12479)

* feat(rust): support various Rust integer types (#2)

* fix: Use ROOT locale

* fix: unsigned int bounds were incorrect

* fix: deal with potential null value
This commit is contained in:
thrykol
2022-07-09 06:37:16 -06:00
committed by GitHub
parent d364daa5af
commit a1892b1636
4 changed files with 261 additions and 8 deletions

View File

@@ -18,11 +18,13 @@ These options may be applied as additional-properties (cli) or configOptions (pl
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|bestFitInt|Use best fitting integer type where minimum or maximum is set| |false|
|enumNameSuffix|Suffix that will be appended to all enum names.| ||
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
|library|library template (sub-template) to use.|<dl><dt>**hyper**</dt><dd>HTTP client: Hyper.</dd><dt>**reqwest**</dt><dd>HTTP client: Reqwest.</dd></dl>|reqwest|
|packageName|Rust package name (convention: lowercase).| |openapi|
|packageVersion|Rust package version.| |1.0.0|
|preferUnsignedInt|Prefer unsigned integers where minimum value is &gt;= 0| |false|
|supportAsync|If set, generate async function call instead. This option is for 'reqwest' library only| |true|
|supportMultipleResponses|If set, return type wraps an enum of all possible 2xx schemas. This option is for 'reqwest' library only| |false|
|useSingleRequestParameter|Setting this property to true will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter.| |false|

View File

@@ -39,6 +39,7 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.*;
import static org.openapitools.codegen.utils.StringUtils.camelize;
@@ -50,6 +51,8 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
private boolean supportAsync = true;
private boolean supportMultipleResponses = false;
private boolean withAWSV4Signature = false;
private boolean preferUnsignedInt = false;
private boolean bestFitInt = false;
public static final String PACKAGE_NAME = "packageName";
public static final String PACKAGE_VERSION = "packageVersion";
@@ -57,6 +60,8 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
public static final String REQWEST_LIBRARY = "reqwest";
public static final String SUPPORT_ASYNC = "supportAsync";
public static final String SUPPORT_MULTIPLE_RESPONSES = "supportMultipleResponses";
public static final String PREFER_UNSIGNED_INT = "preferUnsignedInt";
public static final String BEST_FIT_INT = "bestFitInt";
protected String packageName = "openapi";
protected String packageVersion = "1.0.0";
@@ -65,6 +70,7 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
protected String apiFolder = "src/apis";
protected String modelFolder = "src/models";
protected String enumSuffix = ""; // default to empty string for backward compatibility
protected Map<String, String> unsignedMapping;
public CodegenType getTag() {
return CodegenType.CLIENT;
@@ -174,6 +180,12 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
typeMapping.put("object", "serde_json::Value");
typeMapping.put("AnyType", "serde_json::Value");
unsignedMapping = new HashMap<>();
unsignedMapping.put("i8", "u8");
unsignedMapping.put("i16", "u16");
unsignedMapping.put("i32", "u32");
unsignedMapping.put("i64", "u64");
// no need for rust
//importMapping = new HashMap<String, String>();
@@ -193,6 +205,10 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
cliOptions.add(new CliOption(CodegenConstants.ENUM_NAME_SUFFIX, CodegenConstants.ENUM_NAME_SUFFIX_DESC).defaultValue(this.enumSuffix));
cliOptions.add(new CliOption(CodegenConstants.WITH_AWSV4_SIGNATURE_COMMENT, CodegenConstants.WITH_AWSV4_SIGNATURE_COMMENT_DESC, SchemaTypeUtil.BOOLEAN_TYPE)
.defaultValue(Boolean.FALSE.toString()));
cliOptions.add(new CliOption(PREFER_UNSIGNED_INT, "Prefer unsigned integers where minimum value is >= 0", SchemaTypeUtil.BOOLEAN_TYPE)
.defaultValue(Boolean.FALSE.toString()));
cliOptions.add(new CliOption(BEST_FIT_INT, "Use best fitting integer type where minimum or maximum is set", SchemaTypeUtil.BOOLEAN_TYPE)
.defaultValue(Boolean.FALSE.toString()));
supportedLibraries.put(HYPER_LIBRARY, "HTTP client: Hyper.");
supportedLibraries.put(REQWEST_LIBRARY, "HTTP client: Reqwest.");
@@ -296,6 +312,16 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
}
writePropertyBack(SUPPORT_MULTIPLE_RESPONSES, getSupportMultipleReturns());
if (additionalProperties.containsKey(PREFER_UNSIGNED_INT)) {
this.setPreferUnsignedInt(convertPropertyToBoolean(PREFER_UNSIGNED_INT));
}
writePropertyBack(PREFER_UNSIGNED_INT, getPreferUnsignedInt());
if (additionalProperties.containsKey(BEST_FIT_INT)) {
this.setBestFitInt(convertPropertyToBoolean(BEST_FIT_INT));
}
writePropertyBack(BEST_FIT_INT, getBestFitInt());
additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName);
additionalProperties.put(CodegenConstants.PACKAGE_VERSION, packageVersion);
@@ -360,6 +386,22 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
this.supportMultipleResponses = supportMultipleResponses;
}
public boolean getPreferUnsignedInt() {
return preferUnsignedInt;
}
public void setPreferUnsignedInt(boolean preferUnsignedInt) {
this.preferUnsignedInt = preferUnsignedInt;
}
public boolean getBestFitInt() {
return bestFitInt;
}
public void setBestFitInt(boolean bestFitInt) {
this.bestFitInt = bestFitInt;
}
private boolean getUseSingleRequestParameter() {
return useSingleRequestParameter;
}
@@ -414,7 +456,8 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
@Override
public String toParamName(String name) {
return toVarName(name);
// $ref appears to be all uppercase which is contrary to rustfmt practice so lowercase parameters
return toVarName(name.toLowerCase(Locale.ROOT));
}
@Override
@@ -525,10 +568,44 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
@Override
public String getSchemaType(Schema p) {
String schemaType = super.getSchemaType(p);
if (typeMapping.containsKey(schemaType)) {
return typeMapping.get(schemaType);
String type = typeMapping.getOrDefault(schemaType, schemaType);
boolean bestFit = convertPropertyToBoolean(BEST_FIT_INT);
boolean unsigned = convertPropertyToBoolean(PREFER_UNSIGNED_INT);
if (bestFit || unsigned) {
BigDecimal maximum = p.getMaximum();
BigDecimal minimum = p.getMinimum();
try {
if (maximum != null && minimum != null) {
long max = maximum.longValueExact();
long min = minimum.longValueExact();
if (unsigned && bestFit && max <= (Byte.MAX_VALUE * 2) + 1 && min >= 0) {
type = "u8";
} else if (bestFit && max <= Byte.MAX_VALUE && min >= Byte.MIN_VALUE) {
type = "i8";
} else if (unsigned && bestFit && max <= (Short.MAX_VALUE * 2) + 1 && min >= 0) {
type = "u16";
} else if (bestFit && max <= Short.MAX_VALUE && min >= Short.MIN_VALUE) {
type = "i16";
} else if (unsigned && bestFit && max <= (Integer.MAX_VALUE * 2L) + 1 && min >= 0) {
type = "u32";
} else if (bestFit && max <= Integer.MAX_VALUE && min >= Integer.MIN_VALUE) {
type = "i32";
}
}
} catch (ArithmeticException a) {
// no-op; case will be handled in the next if block
}
if (unsigned && minimum != null && minimum.compareTo(BigDecimal.ZERO) >= 0 && unsignedMapping.containsKey(type)) {
type = unsignedMapping.get(type);
}
}
return schemaType;
return type;
}
@Override
@@ -640,7 +717,6 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
return input.replace("*/", "*_/").replace("/*", "/_*");
}
@Override
public String toEnumValue(String value, String datatype) {
if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) {

View File

@@ -75,16 +75,19 @@ impl Request {
self
}
#[allow(unused)]
pub fn with_query_param(mut self, basename: String, param: String) -> Self {
self.query_params.insert(basename, param);
self
}
#[allow(unused)]
pub fn with_path_param(mut self, basename: String, param: String) -> Self {
self.path_params.insert(basename, param);
self
}
#[allow(unused)]
pub fn with_form_param(mut self, basename: String, param: String) -> Self {
self.form_params.insert(basename, param);
self

View File

@@ -17,11 +17,14 @@
package org.openapitools.codegen.rust;
import io.swagger.v3.oas.models.media.IntegerSchema;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.languages.RustClientCodegen;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.math.BigDecimal;
public class RustClientCodegenTest {
@Test
@@ -30,17 +33,31 @@ public class RustClientCodegenTest {
codegen.processOpts();
Assert.assertEquals(codegen.additionalProperties().get(CodegenConstants.HIDE_GENERATION_TIMESTAMP), Boolean.TRUE);
Assert.assertEquals(codegen.isHideGenerationTimestamp(), true);
Assert.assertTrue(codegen.isHideGenerationTimestamp());
Assert.assertEquals(codegen.additionalProperties().get(RustClientCodegen.PREFER_UNSIGNED_INT), Boolean.FALSE);
Assert.assertFalse(codegen.getPreferUnsignedInt());
Assert.assertEquals(codegen.additionalProperties().get(RustClientCodegen.BEST_FIT_INT), Boolean.FALSE);
Assert.assertFalse(codegen.getBestFitInt());
}
@Test
public void testSettersForConfigValues() throws Exception {
final RustClientCodegen codegen = new RustClientCodegen();
codegen.setHideGenerationTimestamp(false);
codegen.setPreferUnsignedInt(true);
codegen.setBestFitInt(true);
codegen.processOpts();
Assert.assertEquals(codegen.additionalProperties().get(CodegenConstants.HIDE_GENERATION_TIMESTAMP), Boolean.FALSE);
Assert.assertEquals(codegen.isHideGenerationTimestamp(), false);
Assert.assertFalse(codegen.isHideGenerationTimestamp());
Assert.assertEquals(codegen.additionalProperties().get(RustClientCodegen.PREFER_UNSIGNED_INT), Boolean.TRUE);
Assert.assertTrue(codegen.getPreferUnsignedInt());
Assert.assertEquals(codegen.additionalProperties().get(RustClientCodegen.BEST_FIT_INT), Boolean.TRUE);
Assert.assertTrue(codegen.getBestFitInt());
}
@Test
@@ -50,7 +67,162 @@ public class RustClientCodegenTest {
codegen.processOpts();
Assert.assertEquals(codegen.additionalProperties().get(CodegenConstants.HIDE_GENERATION_TIMESTAMP), Boolean.FALSE);
Assert.assertEquals(codegen.isHideGenerationTimestamp(), false);
Assert.assertFalse(codegen.isHideGenerationTimestamp());
}
@Test
public void testLowercaseParameterName() throws Exception {
final RustClientCodegen codegen = new RustClientCodegen();
Assert.assertEquals(codegen.toParamName("TESTING"), "testing");
}
@Test
public void testGetSchemaTypeIntegerNoBounds() {
final IntegerSchema s = new IntegerSchema();
final RustClientCodegen codegen = new RustClientCodegen();
codegen.setBestFitInt(true);
codegen.processOpts();
s.setType("i32");
// min and max are null
Assert.assertEquals(codegen.getSchemaType(s), "i32");
s.setMinimum(BigDecimal.valueOf(Short.MIN_VALUE));
// max is null
Assert.assertEquals(codegen.getSchemaType(s), "i32");
s.setMaximum(BigDecimal.valueOf(Short.MAX_VALUE));
s.setMinimum(null);
// min is null
Assert.assertEquals(codegen.getSchemaType(s), "i32");
}
@Test
public void testGetSchemaTypeI64() {
final IntegerSchema s = new IntegerSchema();
final RustClientCodegen codegen = new RustClientCodegen();
codegen.setBestFitInt(true);
codegen.processOpts();
s.setType("i64");
s.setMinimum(BigDecimal.valueOf(Long.MIN_VALUE));
s.setMaximum(BigDecimal.valueOf(Long.MAX_VALUE));
Assert.assertEquals(codegen.getSchemaType(s), "i64");
}
@Test
public void testGetSchemaTypeI32() {
final IntegerSchema s = new IntegerSchema();
final RustClientCodegen codegen = new RustClientCodegen();
codegen.setBestFitInt(true);
codegen.processOpts();
s.setType("i32");
s.setMinimum(BigDecimal.valueOf(Integer.MIN_VALUE));
s.setMaximum(BigDecimal.valueOf(Integer.MAX_VALUE));
Assert.assertEquals(codegen.getSchemaType(s), "i32");
}
@Test
public void testGetSchemaTypeI16() {
final IntegerSchema s = new IntegerSchema();
final RustClientCodegen codegen = new RustClientCodegen();
codegen.setBestFitInt(true);
codegen.processOpts();
s.setType("i32");
s.setMinimum(BigDecimal.valueOf(Short.MIN_VALUE));
s.setMaximum(BigDecimal.valueOf(Short.MAX_VALUE));
Assert.assertEquals(codegen.getSchemaType(s), "i16");
}
@Test
public void testGetSchemaTypeI8() {
final IntegerSchema s = new IntegerSchema();
final RustClientCodegen codegen = new RustClientCodegen();
codegen.setBestFitInt(true);
codegen.processOpts();
s.setType("i32");
s.setMinimum(BigDecimal.valueOf(-128));
s.setMaximum(BigDecimal.valueOf(127));
Assert.assertEquals(codegen.getSchemaType(s), "i8");
}
@Test
public void testGetSchemaTypeU64() {
final IntegerSchema s = new IntegerSchema();
final RustClientCodegen codegen = new RustClientCodegen();
codegen.setPreferUnsignedInt(true);
codegen.processOpts();
s.setType("i64");
s.setMinimum(BigDecimal.ZERO);
Assert.assertEquals(codegen.getSchemaType(s), "u64");
s.setMaximum(BigDecimal.valueOf(Long.MAX_VALUE).add(BigDecimal.valueOf(Long.MAX_VALUE)));
Assert.assertEquals(codegen.getSchemaType(s), "u64");
s.setMinimum(null);
s.setMaximum(null);
Assert.assertEquals(codegen.getSchemaType(s), "i64");
}
@Test
public void testGetSchemaTypeU32() {
final IntegerSchema s = new IntegerSchema();
final RustClientCodegen codegen = new RustClientCodegen();
codegen.setPreferUnsignedInt(true);
codegen.processOpts();
s.setType("i32");
s.setMinimum(BigDecimal.ZERO);
Assert.assertEquals(codegen.getSchemaType(s), "u32");
s.setMaximum(BigDecimal.valueOf(65535));
Assert.assertEquals(codegen.getSchemaType(s), "u32");
}
@Test
public void testGetSchemaTypeU16() {
final IntegerSchema s = new IntegerSchema();
final RustClientCodegen codegen = new RustClientCodegen();
codegen.setBestFitInt(true);
codegen.setPreferUnsignedInt(true);
codegen.processOpts();
s.setType("i32");
s.setMinimum(BigDecimal.ZERO);
s.setMaximum(BigDecimal.valueOf(65535));
Assert.assertEquals(codegen.getSchemaType(s), "u16");
}
@Test
public void testGetSchemaTypeU8() {
final IntegerSchema s = new IntegerSchema();
final RustClientCodegen codegen = new RustClientCodegen();
codegen.setBestFitInt(true);
codegen.setPreferUnsignedInt(true);
codegen.processOpts();
s.setType("i32");
s.setMinimum(BigDecimal.ZERO);
s.setMaximum(BigDecimal.valueOf(255));
Assert.assertEquals(codegen.getSchemaType(s), "u8");
}
}