forked from loafle/openapi-generator-original
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:
@@ -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 >= 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|
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user