Skip to content

Commit 33ef2a0

Browse files
[rust-axum] Fix uint32/uint64 type mapping (#23547)
* fix(rust-axum): fix uint32/uint64 type mapping * fix(rust-axum): preserve explicit unsigned intent in integer fallback * fix(rust-axum): align unsigned handling with RustClient and RustServer behavior * fix(rust-axum): enhance schema type handling for unconstrained integers and array complex types * chore(rust-axum): regenerate samples * test(rust-axum): add `integer-types.yaml` to integration tests * fix(rust-axum): fix malformed Markdown link syntax
1 parent 7275888 commit 33ef2a0

50 files changed

Lines changed: 2174 additions & 90 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
generatorName: rust-axum
2+
outputDir: samples/server/petstore/rust-axum/output/rust-axum-integer-types-test
3+
inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-axum/integer-types.yaml
4+
templateDir: modules/openapi-generator/src/main/resources/rust-axum
5+
generateAliasAsModel: true
6+
additionalProperties:
7+
hideGenerationTimestamp: "true"
8+
packageName: rust-axum-integer-types-test
9+
homePageUrl: https://github.com/openapitools/openapi-generator
10+
globalProperties:
11+
skipFormModel: false
12+
enablePostProcessFile: true

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

Lines changed: 115 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.slf4j.LoggerFactory;
4545

4646
import java.io.File;
47+
import java.math.BigDecimal;
4748
import java.math.BigInteger;
4849
import java.nio.file.Path;
4950
import java.util.*;
@@ -1027,6 +1028,72 @@ public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, S
10271028
return codegenParameter;
10281029
}
10291030

1031+
private String getIntegerDataType(String format,
1032+
BigInteger minimum,
1033+
boolean exclusiveMinimum,
1034+
final BigInteger maximum,
1035+
final boolean exclusiveMaximum) {
1036+
final boolean unsigned = canFitIntoUnsigned(minimum, exclusiveMinimum);
1037+
1038+
if (StringUtils.isEmpty(format)) {
1039+
return bestFittingIntegerType(
1040+
minimum,
1041+
exclusiveMinimum,
1042+
maximum,
1043+
exclusiveMaximum,
1044+
unsigned);
1045+
}
1046+
1047+
switch (format) {
1048+
// custom integer formats (legacy)
1049+
case "uint32":
1050+
return "u32";
1051+
case "uint64":
1052+
return "u64";
1053+
case "int32":
1054+
return unsigned ? "u32" : "i32";
1055+
case "int64":
1056+
return unsigned ? "u64" : "i64";
1057+
default:
1058+
LOGGER.warn("The integer format '{}' is not recognized and will be ignored.", format);
1059+
return bestFittingIntegerType(
1060+
minimum,
1061+
exclusiveMinimum,
1062+
maximum,
1063+
exclusiveMaximum,
1064+
unsigned);
1065+
}
1066+
}
1067+
1068+
@Override
1069+
public String getSchemaType(Schema p) {
1070+
if (Objects.equals(p.getType(), "integer")) {
1071+
final boolean hasNoFormat = StringUtils.isEmpty(p.getFormat());
1072+
final boolean hasNoBounds = p.getMinimum() == null
1073+
&& p.getMaximum() == null
1074+
&& p.getExclusiveMinimum() == null
1075+
&& p.getExclusiveMaximum() == null;
1076+
1077+
// Preserve legacy schema typing for unconstrained integers so alias models
1078+
// keep their expected model resolution flow.
1079+
if (hasNoFormat && hasNoBounds) {
1080+
return super.getSchemaType(p);
1081+
}
1082+
1083+
final BigInteger minimum = Optional.ofNullable(p.getMinimum()).map(BigDecimal::toBigInteger).orElse(null);
1084+
final BigInteger maximum = Optional.ofNullable(p.getMaximum()).map(BigDecimal::toBigInteger).orElse(null);
1085+
1086+
return getIntegerDataType(
1087+
p.getFormat(),
1088+
minimum,
1089+
Optional.ofNullable(p.getExclusiveMinimum()).orElse(false),
1090+
maximum,
1091+
Optional.ofNullable(p.getExclusiveMaximum()).orElse(false));
1092+
}
1093+
1094+
return super.getSchemaType(p);
1095+
}
1096+
10301097
@Override
10311098
public String toInstantiationType(final Schema p) {
10321099
if (ModelUtils.isArraySchema(p)) {
@@ -1040,6 +1107,45 @@ public String toInstantiationType(final Schema p) {
10401107
}
10411108
}
10421109

1110+
@Override
1111+
public CodegenProperty fromProperty(String name, Schema p, boolean required) {
1112+
CodegenProperty property = super.fromProperty(name, p, required);
1113+
ensureArrayComplexType(property);
1114+
return property;
1115+
}
1116+
1117+
@Override
1118+
public CodegenProperty fromProperty(String name, Schema p, boolean required, boolean schemaIsFromAdditionalProperties) {
1119+
CodegenProperty property = super.fromProperty(name, p, required, schemaIsFromAdditionalProperties);
1120+
ensureArrayComplexType(property);
1121+
return property;
1122+
}
1123+
1124+
private void ensureArrayComplexType(CodegenProperty property) {
1125+
if (property == null || !property.isArray || StringUtils.isNotBlank(property.complexType) || property.items == null) {
1126+
return;
1127+
}
1128+
1129+
String candidate = StringUtils.defaultIfBlank(property.items.complexType, property.items.baseType);
1130+
if (StringUtils.isBlank(candidate)) {
1131+
candidate = property.items.dataType;
1132+
}
1133+
if (StringUtils.isBlank(candidate)) {
1134+
return;
1135+
}
1136+
1137+
property.complexType = reverseTypeMapping(candidate);
1138+
}
1139+
1140+
private String reverseTypeMapping(String rustType) {
1141+
for (Map.Entry<String, String> entry : typeMapping.entrySet()) {
1142+
if (Objects.equals(entry.getValue(), rustType)) {
1143+
return entry.getKey();
1144+
}
1145+
}
1146+
return rustType;
1147+
}
1148+
10431149
@Override
10441150
public Map<String, Object> postProcessSupportingFileData(Map<String, Object> bundle) {
10451151
generateYAMLSpecFile(bundle);
@@ -1116,13 +1222,15 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
11161222
}
11171223

11181224
// Integer type fitting
1119-
if (Objects.equals(property.baseType, "integer")) {
1120-
BigInteger minimum = Optional.ofNullable(property.getMinimum()).map(BigInteger::new).orElse(null);
1121-
BigInteger maximum = Optional.ofNullable(property.getMaximum()).map(BigInteger::new).orElse(null);
1122-
property.dataType = bestFittingIntegerType(
1123-
minimum, property.getExclusiveMinimum(),
1124-
maximum, property.getExclusiveMaximum(),
1125-
true);
1225+
if (Boolean.TRUE.equals(property.isInteger) || Boolean.TRUE.equals(property.isLong) || Objects.equals(property.baseType, "UnsignedInteger") || Objects.equals(property.baseType, "UnsignedLong")) {
1226+
final BigInteger minimum = Optional.ofNullable(property.getMinimum()).map(BigInteger::new).orElse(null);
1227+
final BigInteger maximum = Optional.ofNullable(property.getMaximum()).map(BigInteger::new).orElse(null);
1228+
property.dataType = getIntegerDataType(
1229+
property.dataFormat,
1230+
minimum,
1231+
property.getExclusiveMinimum(),
1232+
maximum,
1233+
property.getExclusiveMaximum());
11261234
}
11271235

11281236
property.name = underscore(property.name);

modules/openapi-generator/src/main/resources/rust-axum/README.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ This server was generated by the [openapi-generator]
1111
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
1212
server, you can easily generate a server stub.
1313

14-
To see how to make this your own, look here: [README]((https://openapi-generator.tech))
14+
To see how to make this your own, look here: [README](https://openapi-generator.tech)
1515

1616
- API version: {{{appVersion}}}
1717
{{^hideGenerationTimestamp}}

modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustAxumServerCodegenTest.java

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package org.openapitools.codegen.rust;
22

3+
import io.swagger.v3.oas.models.media.IntegerSchema;
34
import org.openapitools.codegen.DefaultGenerator;
45
import org.openapitools.codegen.TestUtils;
56
import org.openapitools.codegen.config.CodegenConfigurator;
7+
import org.openapitools.codegen.languages.RustAxumServerCodegen;
8+
import org.testng.Assert;
69
import org.testng.annotations.Test;
710

811
import java.io.File;
912
import java.io.IOException;
13+
import java.math.BigDecimal;
1014
import java.nio.file.Files;
1115
import java.nio.file.Path;
1216
import java.util.List;
@@ -49,4 +53,54 @@ public void testPreventDuplicateOperationDeclaration() throws IOException {
4953
TestUtils.assertFileExists(outputPath);
5054
TestUtils.assertFileContains(outputPath, routerSpec);
5155
}
52-
}
56+
57+
@Test
58+
public void testIntegerSchemaTypeMapping() {
59+
RustAxumServerCodegen codegen = new RustAxumServerCodegen();
60+
IntegerSchema schema = new IntegerSchema();
61+
62+
schema.setFormat("uint32");
63+
Assert.assertEquals(codegen.getSchemaType(schema), "u32");
64+
65+
schema = new IntegerSchema();
66+
schema.setFormat("uint64");
67+
Assert.assertEquals(codegen.getSchemaType(schema), "u64");
68+
69+
schema = new IntegerSchema();
70+
schema.setFormat("int32");
71+
schema.setMinimum(BigDecimal.ZERO);
72+
Assert.assertEquals(codegen.getSchemaType(schema), "u32");
73+
74+
schema = new IntegerSchema();
75+
schema.setFormat("int64");
76+
schema.setMinimum(BigDecimal.ZERO);
77+
Assert.assertEquals(codegen.getSchemaType(schema), "u64");
78+
79+
schema = new IntegerSchema();
80+
schema.setFormat(null);
81+
schema.setMinimum(BigDecimal.ZERO);
82+
schema.setMaximum(BigDecimal.valueOf(255));
83+
Assert.assertEquals(codegen.getSchemaType(schema), "u8");
84+
}
85+
86+
@Test
87+
public void testGeneratedIntegerTypes() throws IOException {
88+
Path target = Files.createTempDirectory("test");
89+
final CodegenConfigurator configurator = new CodegenConfigurator()
90+
.setGeneratorName("rust-axum")
91+
.setInputSpec("src/test/resources/3_0/rust-axum/integer-types.yaml")
92+
.setSkipOverwrite(false)
93+
.setOutputDir(target.toAbsolutePath().toString().replace("\\", "/"));
94+
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
95+
files.forEach(File::deleteOnExit);
96+
97+
Path modelsPath = Path.of(target.toString(), "/src/models.rs");
98+
TestUtils.assertFileExists(modelsPath);
99+
TestUtils.assertFileContains(modelsPath, "pub legacy_uint32: u32");
100+
TestUtils.assertFileContains(modelsPath, "pub legacy_uint64: u64");
101+
TestUtils.assertFileContains(modelsPath, "pub positive_int32: u32");
102+
TestUtils.assertFileContains(modelsPath, "pub positive_int64: u64");
103+
TestUtils.assertFileContains(modelsPath, "pub small_positive: u8");
104+
TestUtils.assertFileContains(modelsPath, "pub struct GetIntegersQueryParams");
105+
}
106+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
openapi: 3.0.3
2+
info:
3+
title: Rust Axum Integer Type Mapping Test
4+
version: 1.0.0
5+
paths:
6+
/integers:
7+
get:
8+
operationId: getIntegers
9+
parameters:
10+
- name: legacy_uint32
11+
in: query
12+
required: true
13+
schema:
14+
type: integer
15+
format: uint32
16+
- name: legacy_uint64
17+
in: query
18+
required: true
19+
schema:
20+
type: integer
21+
format: uint64
22+
- name: positive_int32
23+
in: query
24+
required: true
25+
schema:
26+
type: integer
27+
format: int32
28+
minimum: 0
29+
- name: positive_int64
30+
in: query
31+
required: true
32+
schema:
33+
type: integer
34+
format: int64
35+
minimum: 0
36+
- name: small_positive
37+
in: query
38+
required: true
39+
schema:
40+
type: integer
41+
minimum: 0
42+
maximum: 255
43+
responses:
44+
'200':
45+
description: OK
46+
content:
47+
application/json:
48+
schema:
49+
$ref: '#/components/schemas/IntegerTypes'
50+
components:
51+
schemas:
52+
IntegerTypes:
53+
type: object
54+
required:
55+
- legacy_uint32
56+
- legacy_uint64
57+
- positive_int32
58+
- positive_int64
59+
- small_positive
60+
properties:
61+
legacy_uint32:
62+
type: integer
63+
format: uint32
64+
legacy_uint64:
65+
type: integer
66+
format: uint64
67+
positive_int32:
68+
type: integer
69+
format: int32
70+
minimum: 0
71+
positive_int64:
72+
type: integer
73+
format: int64
74+
minimum: 0
75+
small_positive:
76+
type: integer
77+
minimum: 0
78+
maximum: 255
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7.19.0-SNAPSHOT
1+
7.22.0-SNAPSHOT

samples/server/petstore/rust-axum/output/apikey-authorization/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ This server was generated by the [openapi-generator]
99
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
1010
server, you can easily generate a server stub.
1111

12-
To see how to make this your own, look here: [README]((https://openapi-generator.tech))
12+
To see how to make this your own, look here: [README](https://openapi-generator.tech)
1313

1414
- API version: 1.0.0
15-
- Generator version: 7.19.0-SNAPSHOT
15+
- Generator version: 7.22.0-SNAPSHOT
1616

1717

1818

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7.19.0-SNAPSHOT
1+
7.22.0-SNAPSHOT

samples/server/petstore/rust-axum/output/apikey-auths/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ This server was generated by the [openapi-generator]
99
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
1010
server, you can easily generate a server stub.
1111

12-
To see how to make this your own, look here: [README]((https://openapi-generator.tech))
12+
To see how to make this your own, look here: [README](https://openapi-generator.tech)
1313

1414
- API version: 1.0.0
15-
- Generator version: 7.19.0-SNAPSHOT
15+
- Generator version: 7.22.0-SNAPSHOT
1616

1717

1818

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7.19.0-SNAPSHOT
1+
7.22.0-SNAPSHOT

0 commit comments

Comments
 (0)