Skip to content

Commit f1935af

Browse files
authored
fix(rust): use serde_repr for integer property enums (#23580)
1 parent 230e288 commit f1935af

5 files changed

Lines changed: 86 additions & 3 deletions

File tree

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,14 @@ public String toEnumName(CodegenProperty property) {
391391

392392
@Override
393393
public String toEnumValue(String value, String datatype) {
394-
// This is the representation of the enum that will be serialized / deserialized
395-
// Note: generators currently only support string enums, so checking the type here is pointless
394+
if ("integer".equals(datatype) || "number".equals(datatype)
395+
|| "i8".equals(datatype) || "i16".equals(datatype)
396+
|| "i32".equals(datatype) || "i64".equals(datatype)
397+
|| "u8".equals(datatype) || "u16".equals(datatype)
398+
|| "u32".equals(datatype) || "u64".equals(datatype)
399+
|| "f32".equals(datatype) || "f64".equals(datatype)) {
400+
return value;
401+
}
396402
return escapeText(value);
397403
}
398404

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,16 @@ public ModelsMap postProcessModels(ModelsMap objs) {
379379
}
380380
}
381381

382+
// Flag structs with integer-enum properties so the template can emit serde_repr import once
383+
if (!cm.isEnum) {
384+
for (CodegenProperty cp : cm.vars) {
385+
if (cp.isEnum && cp.isInteger) {
386+
cm.vendorExtensions.put("x-rust-has-integer-property-enum", true);
387+
break;
388+
}
389+
}
390+
}
391+
382392
// Compute documentation type for each property
383393
// This matches the actual generated code type, including HashSet for uniqueItems
384394
for (CodegenProperty cp : cm.vars) {

modules/openapi-generator/src/main/resources/rust/model.mustache

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use serde::{Deserialize, Serialize};
55
{{#model}}
66
{{^isEnum}}{{#vendorExtensions.x-rust-has-byte-array}}
77
use serde_with::serde_as;
8-
{{/vendorExtensions.x-rust-has-byte-array}}{{/isEnum}}
8+
{{/vendorExtensions.x-rust-has-byte-array}}{{#vendorExtensions.x-rust-has-integer-property-enum}}
9+
use serde_repr::{Serialize_repr,Deserialize_repr};
10+
{{/vendorExtensions.x-rust-has-integer-property-enum}}{{/isEnum}}
911
{{#isEnum}}
1012
{{#isInteger}}
1113
use serde_repr::{Serialize_repr,Deserialize_repr};
@@ -200,6 +202,31 @@ impl Default for {{classname}} {
200202
{{!-- for properties that are of enum type --}}
201203
{{#vars}}
202204
{{#isEnum}}
205+
{{#isInteger}}
206+
/// {{{description}}}
207+
#[repr(i64)]
208+
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize_repr, Deserialize_repr)]
209+
pub enum {{{enumName}}} {
210+
{{#allowableValues}}
211+
{{#enumVars}}
212+
{{{name}}} = {{{value}}},
213+
{{/enumVars}}
214+
{{/allowableValues}}
215+
}
216+
217+
impl std::fmt::Display for {{{enumName}}} {
218+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219+
write!(f, "{}", match self {
220+
{{#allowableValues}}
221+
{{#enumVars}}
222+
Self::{{{name}}} => "{{{value}}}",
223+
{{/enumVars}}
224+
{{/allowableValues}}
225+
})
226+
}
227+
}
228+
{{/isInteger}}
229+
{{^isInteger}}
203230
/// {{{description}}}
204231
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
205232
pub enum {{{enumName}}} {
@@ -210,6 +237,7 @@ pub enum {{{enumName}}} {
210237
{{/enumVars}}
211238
{{/allowableValues}}
212239
}
240+
{{/isInteger}}
213241

214242
impl Default for {{{enumName}}} {
215243
fn default() -> {{{enumName}}} {

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,27 @@ public void testMultipleArrayTypesEnum() throws IOException {
272272
TestUtils.assertFileContains(outputPath, enumSpec);
273273
}
274274

275+
@Test
276+
public void testIntegerPropertyEnum() throws IOException {
277+
Path target = Files.createTempDirectory("test");
278+
target.toFile().deleteOnExit();
279+
final CodegenConfigurator configurator = new CodegenConfigurator()
280+
.setGeneratorName("rust")
281+
.setInputSpec("src/test/resources/3_1/rust_integer_property_enum.yaml")
282+
.setSkipOverwrite(false)
283+
.setOutputDir(target.toAbsolutePath().toString().replace("\\", "/"));
284+
new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
285+
Path outputPath = Path.of(target.toString(), "/src/models/signed_message_signature.rs");
286+
TestUtils.assertFileExists(outputPath);
287+
// The integer-enum property must use serde_repr (not string rename)
288+
TestUtils.assertFileContains(outputPath, "use serde_repr::{Serialize_repr,Deserialize_repr}");
289+
TestUtils.assertFileContains(outputPath, "Serialize_repr, Deserialize_repr");
290+
TestUtils.assertFileContains(outputPath, "= 0");
291+
TestUtils.assertFileContains(outputPath, "= 1");
292+
// Must NOT contain a string rename for an integer variant
293+
TestUtils.assertFileNotContains(outputPath, linearize("#[serde(rename = \"0\")]"));
294+
}
295+
275296
@Test
276297
public void testArrayWithObjectEnumValues() throws IOException {
277298
Path target = Files.createTempDirectory("test");
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
openapi: 3.1.0
2+
info:
3+
title: Integer property enum
4+
description: Struct with a type:integer property constrained to an enum should use serde_repr, not string rename.
5+
version: 1.0.0
6+
paths: {}
7+
components:
8+
schemas:
9+
SignedMessageSignature:
10+
type: object
11+
properties:
12+
v:
13+
type: integer
14+
enum: [0, 1]
15+
r:
16+
type: string
17+
s:
18+
type: string

0 commit comments

Comments
 (0)