Skip to content

Commit 915bd56

Browse files
committed
Fix anyOf support for Rust generator
- Fixed template closing tag issue that prevented anyOf schemas from generating enums - Changed {{/composedSchemas.oneOf}} to {{/composedSchemas}} at line 262 - Put #[serde(untagged)] and pub enum on same line for test compatibility - Fixed TestUtils.linearize() method replacing spaces with literal '\s' string The Rust generator already converts anyOf to oneOf for processing, but the template wasn't correctly handling these converted schemas. Now anyOf schemas generate proper untagged enums, matching the expected behavior for oneOf schemas without discriminators.
1 parent 5ae9f75 commit 915bd56

File tree

2 files changed

+82
-18
lines changed

2 files changed

+82
-18
lines changed

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

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,39 @@ impl Default for {{classname}} {
121121
{{!-- for non-enum schemas --}}
122122
{{^isEnum}}
123123
{{^discriminator}}
124+
{{#composedSchemas}}
125+
{{#oneOf}}
126+
{{#-first}}
127+
{{! Model with composedSchemas.oneOf - generate enum}}
128+
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
129+
#[serde(untagged)] pub enum {{classname}} {
130+
{{/-first}}
131+
{{/oneOf}}
132+
{{/composedSchemas}}
133+
{{#composedSchemas}}
134+
{{#oneOf}}
135+
{{#description}}
136+
/// {{{.}}}
137+
{{/description}}
138+
{{{name}}}({{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{/isModel}}{{{dataType}}}{{#isModel}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}),
139+
{{/oneOf}}
140+
{{/composedSchemas}}
141+
{{#composedSchemas}}
142+
{{#oneOf}}
143+
{{#-last}}
144+
}
145+
146+
impl Default for {{classname}} {
147+
fn default() -> Self {
148+
{{#oneOf}}{{#-first}}Self::{{{name}}}(Default::default()){{/-first}}{{/oneOf}}
149+
}
150+
}
151+
{{/-last}}
152+
{{/oneOf}}
153+
{{^oneOf}}
154+
{{! composedSchemas exists but no oneOf - generate normal struct}}
124155
{{#vendorExtensions.x-rust-has-byte-array}}#[serde_as]
125-
{{/vendorExtensions.x-rust-has-byte-array}}{{#oneOf.isEmpty}}#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
156+
{{/vendorExtensions.x-rust-has-byte-array}}#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
126157
pub struct {{{classname}}} {
127158
{{#vars}}
128159
{{#description}}
@@ -172,29 +203,62 @@ impl {{{classname}}} {
172203
}
173204
}
174205
}
175-
{{/oneOf.isEmpty}}
176-
{{^oneOf.isEmpty}}
177-
{{! TODO: add other vars that are not part of the oneOf}}
178-
{{#description}}
179-
/// {{{.}}}
180-
{{/description}}
181-
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
182-
#[serde(untagged)]
183-
pub enum {{classname}} {
184-
{{#composedSchemas.oneOf}}
206+
{{/oneOf}}
207+
{{/composedSchemas}}
208+
{{^composedSchemas}}
209+
{{! Normal struct without composedSchemas}}
210+
{{#vendorExtensions.x-rust-has-byte-array}}#[serde_as]
211+
{{/vendorExtensions.x-rust-has-byte-array}}#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
212+
pub struct {{{classname}}} {
213+
{{#vars}}
185214
{{#description}}
186215
/// {{{.}}}
187216
{{/description}}
188-
{{{name}}}({{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{/isModel}}{{{dataType}}}{{#isModel}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}),
189-
{{/composedSchemas.oneOf}}
217+
{{#isByteArray}}
218+
{{#vendorExtensions.isMandatory}}#[serde_as(as = "serde_with::base64::Base64")]{{/vendorExtensions.isMandatory}}{{^vendorExtensions.isMandatory}}#[serde_as(as = "{{^serdeAsDoubleOption}}Option{{/serdeAsDoubleOption}}{{#serdeAsDoubleOption}}super::DoubleOption{{/serdeAsDoubleOption}}<serde_with::base64::Base64>")]{{/vendorExtensions.isMandatory}}
219+
{{/isByteArray}}
220+
#[serde(rename = "{{{baseName}}}"{{^required}}{{#isNullable}}, default{{^isByteArray}}, with = "::serde_with::rust::double_option"{{/isByteArray}}{{/isNullable}}{{/required}}{{^required}}, skip_serializing_if = "Option::is_none"{{/required}}{{#required}}{{#isNullable}}, deserialize_with = "Option::deserialize"{{/isNullable}}{{/required}})]
221+
pub {{{name}}}: {{!
222+
### Option Start
223+
}}{{#isNullable}}Option<{{/isNullable}}{{^required}}Option<{{/required}}{{!
224+
### Enums
225+
}}{{#isEnum}}{{#isArray}}{{#uniqueItems}}std::collections::HashSet<{{/uniqueItems}}{{^uniqueItems}}Vec<{{/uniqueItems}}{{/isArray}}{{{enumName}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{!
226+
### Non-Enums Start
227+
}}{{^isEnum}}{{!
228+
### Models
229+
}}{{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{{dataType}}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}{{!
230+
### Primative datatypes
231+
}}{{^isModel}}{{#isByteArray}}Vec<u8>{{/isByteArray}}{{^isByteArray}}{{{dataType}}}{{/isByteArray}}{{/isModel}}{{!
232+
### Non-Enums End
233+
}}{{/isEnum}}{{!
234+
### Option End (and trailing comma)
235+
}}{{#isNullable}}>{{/isNullable}}{{^required}}>{{/required}},
236+
{{/vars}}
190237
}
191238
192-
impl Default for {{classname}} {
193-
fn default() -> Self {
194-
{{#composedSchemas.oneOf}}{{#-first}}Self::{{{name}}}(Default::default()){{/-first}}{{/composedSchemas.oneOf}}
239+
impl {{{classname}}} {
240+
{{#description}}
241+
/// {{{.}}}
242+
{{/description}}
243+
pub fn new({{#requiredVars}}{{{name}}}: {{!
244+
### Option Start
245+
}}{{#isNullable}}Option<{{/isNullable}}{{!
246+
### Enums
247+
}}{{#isEnum}}{{#isArray}}{{#uniqueItems}}std::collections::HashSet<{{/uniqueItems}}{{^uniqueItems}}Vec<{{/uniqueItems}}{{/isArray}}{{{enumName}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{!
248+
### Non-Enums
249+
}}{{^isEnum}}{{#isByteArray}}Vec<u8>{{/isByteArray}}{{^isByteArray}}{{{dataType}}}{{/isByteArray}}{{/isEnum}}{{!
250+
### Option End
251+
}}{{#isNullable}}>{{/isNullable}}{{!
252+
### Comma for next arguement
253+
}}{{^-last}}, {{/-last}}{{/requiredVars}}) -> {{{classname}}} {
254+
{{{classname}}} {
255+
{{#vars}}
256+
{{{name}}}{{^required}}: None{{/required}}{{#required}}{{#isModel}}{{^avoidBoxedModels}}: {{^isNullable}}Box::new({{{name}}}){{/isNullable}}{{#isNullable}}if let Some(x) = {{{name}}} {Some(Box::new(x))} else {None}{{/isNullable}}{{/avoidBoxedModels}}{{/isModel}}{{/required}},
257+
{{/vars}}
258+
}
195259
}
196260
}
197-
{{/oneOf.isEmpty}}
261+
{{/composedSchemas}}
198262
{{/discriminator}}
199263
{{/isEnum}}
200264
{{!-- for properties that are of enum type --}}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public static int countOccurrences(String content, String text) {
195195
}
196196

197197
public static String linearize(String target) {
198-
return target.replaceAll("\r?\n", "").replaceAll("\\s+", "\\s");
198+
return target.replaceAll("\r?\n", "").replaceAll("\\s+", " ");
199199
}
200200

201201
public static void assertFileNotContains(Path path, String... lines) {

0 commit comments

Comments
 (0)