diff --git a/docs/generators/rust.md b/docs/generators/rust.md
index 04601e32f85e..03303f46ec44 100644
--- a/docs/generators/rust.md
+++ b/docs/generators/rust.md
@@ -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.|
- **hyper**
- HTTP client: Hyper.
- **reqwest**
- HTTP client: Reqwest.
|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|
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java
index 7bcad6fbc571..6e54e1670f95 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java
@@ -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 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();
@@ -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)) {
diff --git a/modules/openapi-generator/src/main/resources/rust/request.rs b/modules/openapi-generator/src/main/resources/rust/request.rs
index 0aa51fa0c592..d53037850212 100644
--- a/modules/openapi-generator/src/main/resources/rust/request.rs
+++ b/modules/openapi-generator/src/main/resources/rust/request.rs
@@ -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
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java
index 76bdc94fdd67..906e55e51577 100644
--- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java
@@ -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");
+ }
}