Skip to content

Commit f861060

Browse files
authored
feat(rust): support various Rust integer types (#2)
1 parent a39d86c commit f861060

File tree

11 files changed

+554
-59
lines changed

11 files changed

+554
-59
lines changed

docs/generators/rust.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ These options may be applied as additional-properties (cli) or configOptions (pl
1818

1919
| Option | Description | Values | Default |
2020
| ------ | ----------- | ------ | ------- |
21+
|bestFitInt|Use best fitting integer type where minimum or maximum is set| |false|
2122
|enumNameSuffix|Suffix that will be appended to all enum names.| ||
2223
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
2324
|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|
2425
|packageName|Rust package name (convention: lowercase).| |openapi|
2526
|packageVersion|Rust package version.| |1.0.0|
27+
|preferUnsignedInt|Prefer unsigned integers where minimum value is &gt;= 0| |false|
2628
|supportAsync|If set, generate async function call instead. This option is for 'reqwest' library only| |true|
2729
|supportMultipleResponses|If set, return type wraps an enum of all possible 2xx schemas. This option is for 'reqwest' library only| |false|
2830
|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|

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public HaskellServantCodegen() {
8686
.wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML))
8787
.securityFeatures(EnumSet.of(
8888
SecurityFeature.BasicAuth,
89+
SecurityFeature.BearerToken,
8990
SecurityFeature.ApiKey,
9091
SecurityFeature.OAuth2_Implicit
9192
))

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.io.File;
4040
import java.io.IOException;
4141
import java.io.Writer;
42+
import java.math.BigDecimal;
4243
import java.util.*;
4344

4445
import static org.openapitools.codegen.utils.StringUtils.camelize;
@@ -50,13 +51,17 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
5051
private boolean supportAsync = true;
5152
private boolean supportMultipleResponses = false;
5253
private boolean withAWSV4Signature = false;
54+
private boolean preferUnsignedInt = false;
55+
private boolean bestFitInt = false;
5356

5457
public static final String PACKAGE_NAME = "packageName";
5558
public static final String PACKAGE_VERSION = "packageVersion";
5659
public static final String HYPER_LIBRARY = "hyper";
5760
public static final String REQWEST_LIBRARY = "reqwest";
5861
public static final String SUPPORT_ASYNC = "supportAsync";
5962
public static final String SUPPORT_MULTIPLE_RESPONSES = "supportMultipleResponses";
63+
public static final String PREFER_UNSIGNED_INT = "preferUnsignedInt";
64+
public static final String BEST_FIT_INT = "bestFitInt";
6065

6166
protected String packageName = "openapi";
6267
protected String packageVersion = "1.0.0";
@@ -65,6 +70,7 @@ public class RustClientCodegen extends DefaultCodegen implements CodegenConfig {
6570
protected String apiFolder = "src/apis";
6671
protected String modelFolder = "src/models";
6772
protected String enumSuffix = ""; // default to empty string for backward compatibility
73+
protected Map<String, String> unsignedMapping;
6874

6975
public CodegenType getTag() {
7076
return CodegenType.CLIENT;
@@ -174,6 +180,12 @@ public RustClientCodegen() {
174180
typeMapping.put("object", "serde_json::Value");
175181
typeMapping.put("AnyType", "serde_json::Value");
176182

183+
unsignedMapping = new HashMap<>();
184+
unsignedMapping.put("i8", "u8");
185+
unsignedMapping.put("i16", "u16");
186+
unsignedMapping.put("i32", "u32");
187+
unsignedMapping.put("i64", "u64");
188+
177189
// no need for rust
178190
//importMapping = new HashMap<String, String>();
179191

@@ -193,6 +205,10 @@ public RustClientCodegen() {
193205
cliOptions.add(new CliOption(CodegenConstants.ENUM_NAME_SUFFIX, CodegenConstants.ENUM_NAME_SUFFIX_DESC).defaultValue(this.enumSuffix));
194206
cliOptions.add(new CliOption(CodegenConstants.WITH_AWSV4_SIGNATURE_COMMENT, CodegenConstants.WITH_AWSV4_SIGNATURE_COMMENT_DESC, SchemaTypeUtil.BOOLEAN_TYPE)
195207
.defaultValue(Boolean.FALSE.toString()));
208+
cliOptions.add(new CliOption(PREFER_UNSIGNED_INT, "Prefer unsigned integers where minimum value is >= 0", SchemaTypeUtil.BOOLEAN_TYPE)
209+
.defaultValue(Boolean.FALSE.toString()));
210+
cliOptions.add(new CliOption(BEST_FIT_INT, "Use best fitting integer type where minimum or maximum is set", SchemaTypeUtil.BOOLEAN_TYPE)
211+
.defaultValue(Boolean.FALSE.toString()));
196212

197213
supportedLibraries.put(HYPER_LIBRARY, "HTTP client: Hyper.");
198214
supportedLibraries.put(REQWEST_LIBRARY, "HTTP client: Reqwest.");
@@ -296,6 +312,16 @@ public void processOpts() {
296312
}
297313
writePropertyBack(SUPPORT_MULTIPLE_RESPONSES, getSupportMultipleReturns());
298314

315+
if (additionalProperties.containsKey(PREFER_UNSIGNED_INT)) {
316+
this.setPreferUnsignedInt(convertPropertyToBoolean(PREFER_UNSIGNED_INT));
317+
}
318+
writePropertyBack(PREFER_UNSIGNED_INT, getPreferUnsignedInt());
319+
320+
if (additionalProperties.containsKey(BEST_FIT_INT)) {
321+
this.setBestFitInt(convertPropertyToBoolean(BEST_FIT_INT));
322+
}
323+
writePropertyBack(BEST_FIT_INT, getBestFitInt());
324+
299325
additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName);
300326
additionalProperties.put(CodegenConstants.PACKAGE_VERSION, packageVersion);
301327

@@ -360,6 +386,22 @@ public void setSupportMultipleReturns(boolean supportMultipleResponses) {
360386
this.supportMultipleResponses = supportMultipleResponses;
361387
}
362388

389+
public boolean getPreferUnsignedInt() {
390+
return preferUnsignedInt;
391+
}
392+
393+
public void setPreferUnsignedInt(boolean preferUnsignedInt) {
394+
this.preferUnsignedInt = preferUnsignedInt;
395+
}
396+
397+
public boolean getBestFitInt() {
398+
return bestFitInt;
399+
}
400+
401+
public void setBestFitInt(boolean bestFitInt) {
402+
this.bestFitInt = bestFitInt;
403+
}
404+
363405
private boolean getUseSingleRequestParameter() {
364406
return useSingleRequestParameter;
365407
}
@@ -414,7 +456,8 @@ public String toVarName(String name) {
414456

415457
@Override
416458
public String toParamName(String name) {
417-
return toVarName(name);
459+
// $ref appears to be all uppercase which is contrary to rustfmt practice so lowercase parameters
460+
return toVarName(name.toLowerCase());
418461
}
419462

420463
@Override
@@ -525,10 +568,43 @@ public String getTypeDeclaration(Schema p) {
525568
@Override
526569
public String getSchemaType(Schema p) {
527570
String schemaType = super.getSchemaType(p);
528-
if (typeMapping.containsKey(schemaType)) {
529-
return typeMapping.get(schemaType);
571+
String type = typeMapping.getOrDefault(schemaType, schemaType);
572+
573+
if (convertPropertyToBoolean(BEST_FIT_INT)) {
574+
try {
575+
BigDecimal maximum = p.getMaximum();
576+
BigDecimal minimum = p.getMinimum();
577+
if (maximum != null && minimum != null) {
578+
long max = maximum.longValueExact();
579+
long min = minimum.longValueExact();
580+
581+
if (max <= 255 && min >= -256) {
582+
type = "i8";
583+
}
584+
else if (max <= Short.MAX_VALUE && min >= Short.MIN_VALUE) {
585+
type = "i16";
586+
}
587+
else if (max <= Integer.MAX_VALUE && min >= Integer.MIN_VALUE) {
588+
type = "i32";
589+
}
590+
}
591+
} catch (ArithmeticException a) {
592+
// no-op: use the base type
593+
}
530594
}
531-
return schemaType;
595+
596+
if (convertPropertyToBoolean(PREFER_UNSIGNED_INT) && unsignedMapping.containsKey(type)) {
597+
try {
598+
BigDecimal minimum = p.getMinimum();
599+
if (minimum != null && minimum.longValueExact() >= 0) {
600+
type = unsignedMapping.get(type);
601+
}
602+
} catch (ArithmeticException a) {
603+
// no-op: use the base type
604+
}
605+
}
606+
607+
return type;
532608
}
533609

534610
@Override
@@ -640,7 +716,6 @@ public String escapeUnsafeCharacters(String input) {
640716
return input.replace("*/", "*_/").replace("/*", "/_*");
641717
}
642718

643-
644719
@Override
645720
public String toEnumValue(String value, String datatype) {
646721
if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) {

0 commit comments

Comments
 (0)