Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions bin/configs/manual/rust-axum-integer-types.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
generatorName: rust-axum
outputDir: samples/server/petstore/rust-axum/output/rust-axum-integer-types-test
inputSpec: modules/openapi-generator/src/test/resources/3_0/rust-axum/integer-types.yaml
templateDir: modules/openapi-generator/src/main/resources/rust-axum
generateAliasAsModel: true
additionalProperties:
hideGenerationTimestamp: "true"
packageName: rust-axum-integer-types-test
homePageUrl: https://github.com/openapitools/openapi-generator
globalProperties:
skipFormModel: false
enablePostProcessFile: true
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.slf4j.LoggerFactory;

import java.io.File;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.file.Path;
import java.util.*;
Expand Down Expand Up @@ -1024,6 +1025,72 @@ public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, S
return codegenParameter;
}

private String getIntegerDataType(String format,
BigInteger minimum,
boolean exclusiveMinimum,
final BigInteger maximum,
final boolean exclusiveMaximum) {
final boolean unsigned = canFitIntoUnsigned(minimum, exclusiveMinimum);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Explicit unsigned schema intent is no longer considered in integer type resolution, so unconstrained unsigned schemas can regress to signed Rust types.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustAxumServerCodegen.java, line 1033:

<comment>Explicit unsigned schema intent is no longer considered in integer type resolution, so unconstrained unsigned schemas can regress to signed Rust types.</comment>

<file context>
@@ -1028,16 +1028,9 @@ public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, S
-        }
+                                      final BigInteger maximum,
+                                      final boolean exclusiveMaximum) {
+        final boolean unsigned = canFitIntoUnsigned(minimum, exclusiveMinimum);
 
         if (StringUtils.isEmpty(format)) {
</file context>
Fix with Cubic


if (StringUtils.isEmpty(format)) {
return bestFittingIntegerType(
minimum,
exclusiveMinimum,
maximum,
exclusiveMaximum,
unsigned);
}

switch (format) {
// custom integer formats (legacy)
case "uint32":
return "u32";
case "uint64":
return "u64";
case "int32":
return unsigned ? "u32" : "i32";
case "int64":
return unsigned ? "u64" : "i64";
default:
LOGGER.warn("The integer format '{}' is not recognized and will be ignored.", format);
return bestFittingIntegerType(
minimum,
exclusiveMinimum,
maximum,
exclusiveMaximum,
unsigned);
}
}

@Override
public String getSchemaType(Schema p) {
if (Objects.equals(p.getType(), "integer")) {
final boolean hasNoFormat = StringUtils.isEmpty(p.getFormat());
final boolean hasNoBounds = p.getMinimum() == null
&& p.getMaximum() == null
&& p.getExclusiveMinimum() == null
&& p.getExclusiveMaximum() == null;

// Preserve legacy schema typing for unconstrained integers so alias models
// keep their expected model resolution flow.
if (hasNoFormat && hasNoBounds) {
return super.getSchemaType(p);
}

final BigInteger minimum = Optional.ofNullable(p.getMinimum()).map(BigDecimal::toBigInteger).orElse(null);
final BigInteger maximum = Optional.ofNullable(p.getMaximum()).map(BigDecimal::toBigInteger).orElse(null);

return getIntegerDataType(
p.getFormat(),
minimum,
Optional.ofNullable(p.getExclusiveMinimum()).orElse(false),
maximum,
Optional.ofNullable(p.getExclusiveMaximum()).orElse(false));
}

return super.getSchemaType(p);
}

@Override
public String toInstantiationType(final Schema p) {
if (ModelUtils.isArraySchema(p)) {
Expand All @@ -1037,6 +1104,45 @@ public String toInstantiationType(final Schema p) {
}
}

@Override
public CodegenProperty fromProperty(String name, Schema p, boolean required) {
CodegenProperty property = super.fromProperty(name, p, required);
ensureArrayComplexType(property);
return property;
}

@Override
public CodegenProperty fromProperty(String name, Schema p, boolean required, boolean schemaIsFromAdditionalProperties) {
CodegenProperty property = super.fromProperty(name, p, required, schemaIsFromAdditionalProperties);
ensureArrayComplexType(property);
return property;
}

private void ensureArrayComplexType(CodegenProperty property) {
if (property == null || !property.isArray || StringUtils.isNotBlank(property.complexType) || property.items == null) {
return;
}

String candidate = StringUtils.defaultIfBlank(property.items.complexType, property.items.baseType);
if (StringUtils.isBlank(candidate)) {
candidate = property.items.dataType;
}
if (StringUtils.isBlank(candidate)) {
return;
}

property.complexType = reverseTypeMapping(candidate);
}

private String reverseTypeMapping(String rustType) {
for (Map.Entry<String, String> entry : typeMapping.entrySet()) {
if (Objects.equals(entry.getValue(), rustType)) {
return entry.getKey();
}
}
return rustType;
}

@Override
public Map<String, Object> postProcessSupportingFileData(Map<String, Object> bundle) {
generateYAMLSpecFile(bundle);
Expand Down Expand Up @@ -1113,13 +1219,15 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
}

// Integer type fitting
if (Objects.equals(property.baseType, "integer")) {
BigInteger minimum = Optional.ofNullable(property.getMinimum()).map(BigInteger::new).orElse(null);
BigInteger maximum = Optional.ofNullable(property.getMaximum()).map(BigInteger::new).orElse(null);
property.dataType = bestFittingIntegerType(
minimum, property.getExclusiveMinimum(),
maximum, property.getExclusiveMaximum(),
true);
if (Boolean.TRUE.equals(property.isInteger) || Boolean.TRUE.equals(property.isLong) || Objects.equals(property.baseType, "UnsignedInteger") || Objects.equals(property.baseType, "UnsignedLong")) {
final BigInteger minimum = Optional.ofNullable(property.getMinimum()).map(BigInteger::new).orElse(null);
final BigInteger maximum = Optional.ofNullable(property.getMaximum()).map(BigInteger::new).orElse(null);
property.dataType = getIntegerDataType(
property.dataFormat,
minimum,
property.getExclusiveMinimum(),
maximum,
property.getExclusiveMaximum());
}

property.name = underscore(property.name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This server was generated by the [openapi-generator]
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.

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

- API version: {{{appVersion}}}
{{^hideGenerationTimestamp}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package org.openapitools.codegen.rust;

import io.swagger.v3.oas.models.media.IntegerSchema;
import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.TestUtils;
import org.openapitools.codegen.config.CodegenConfigurator;
import org.openapitools.codegen.languages.RustAxumServerCodegen;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
Expand Down Expand Up @@ -49,4 +53,54 @@ public void testPreventDuplicateOperationDeclaration() throws IOException {
TestUtils.assertFileExists(outputPath);
TestUtils.assertFileContains(outputPath, routerSpec);
}
}

@Test
public void testIntegerSchemaTypeMapping() {
RustAxumServerCodegen codegen = new RustAxumServerCodegen();
IntegerSchema schema = new IntegerSchema();

schema.setFormat("uint32");
Assert.assertEquals(codegen.getSchemaType(schema), "u32");

schema = new IntegerSchema();
schema.setFormat("uint64");
Assert.assertEquals(codegen.getSchemaType(schema), "u64");

schema = new IntegerSchema();
schema.setFormat("int32");
schema.setMinimum(BigDecimal.ZERO);
Assert.assertEquals(codegen.getSchemaType(schema), "u32");

schema = new IntegerSchema();
schema.setFormat("int64");
schema.setMinimum(BigDecimal.ZERO);
Assert.assertEquals(codegen.getSchemaType(schema), "u64");

schema = new IntegerSchema();
schema.setFormat(null);
schema.setMinimum(BigDecimal.ZERO);
schema.setMaximum(BigDecimal.valueOf(255));
Assert.assertEquals(codegen.getSchemaType(schema), "u8");
}

@Test
public void testGeneratedIntegerTypes() throws IOException {
Path target = Files.createTempDirectory("test");
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("rust-axum")
.setInputSpec("src/test/resources/3_0/rust-axum/integer-types.yaml")
.setSkipOverwrite(false)
.setOutputDir(target.toAbsolutePath().toString().replace("\\", "/"));
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
files.forEach(File::deleteOnExit);

Path modelsPath = Path.of(target.toString(), "/src/models.rs");
TestUtils.assertFileExists(modelsPath);
TestUtils.assertFileContains(modelsPath, "pub legacy_uint32: u32");
TestUtils.assertFileContains(modelsPath, "pub legacy_uint64: u64");
TestUtils.assertFileContains(modelsPath, "pub positive_int32: u32");
TestUtils.assertFileContains(modelsPath, "pub positive_int64: u64");
TestUtils.assertFileContains(modelsPath, "pub small_positive: u8");
TestUtils.assertFileContains(modelsPath, "pub struct GetIntegersQueryParams");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
openapi: 3.0.3
info:
title: Rust Axum Integer Type Mapping Test
version: 1.0.0
paths:
/integers:
get:
operationId: getIntegers
parameters:
- name: legacy_uint32
in: query
required: true
schema:
type: integer
format: uint32
- name: legacy_uint64
in: query
required: true
schema:
type: integer
format: uint64
- name: positive_int32
in: query
required: true
schema:
type: integer
format: int32
minimum: 0
- name: positive_int64
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
- name: small_positive
in: query
required: true
schema:
type: integer
minimum: 0
maximum: 255
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/IntegerTypes'
components:
schemas:
IntegerTypes:
type: object
required:
- legacy_uint32
- legacy_uint64
- positive_int32
- positive_int64
- small_positive
properties:
legacy_uint32:
type: integer
format: uint32
legacy_uint64:
type: integer
format: uint64
positive_int32:
type: integer
format: int32
minimum: 0
positive_int64:
type: integer
format: int64
minimum: 0
small_positive:
type: integer
minimum: 0
maximum: 255
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7.19.0-SNAPSHOT
7.22.0-SNAPSHOT
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ This server was generated by the [openapi-generator]
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.

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

- API version: 1.0.0
- Generator version: 7.19.0-SNAPSHOT
- Generator version: 7.22.0-SNAPSHOT



Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7.19.0-SNAPSHOT
7.22.0-SNAPSHOT
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ This server was generated by the [openapi-generator]
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.

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

- API version: 1.0.0
- Generator version: 7.19.0-SNAPSHOT
- Generator version: 7.22.0-SNAPSHOT



Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7.19.0-SNAPSHOT
7.22.0-SNAPSHOT
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ This server was generated by the [openapi-generator]
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.

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

- API version: 1.0.7
- Generator version: 7.19.0-SNAPSHOT
- Generator version: 7.22.0-SNAPSHOT



Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7.19.0-SNAPSHOT
7.22.0-SNAPSHOT
4 changes: 2 additions & 2 deletions samples/server/petstore/rust-axum/output/openapi-v3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ This server was generated by the [openapi-generator]
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.

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

- API version: 1.0.7
- Generator version: 7.19.0-SNAPSHOT
- Generator version: 7.22.0-SNAPSHOT



Expand Down
Loading
Loading