diff --git a/bin/utils/test_file_list.yaml b/bin/utils/test_file_list.yaml
index 1d0fb391de0..d9f0ef85e8d 100644
--- a/bin/utils/test_file_list.yaml
+++ b/bin/utils/test_file_list.yaml
@@ -61,4 +61,4 @@
- filename: "samples/server/petstore/rust-axum/output/rust-axum-oneof/tests/oneof_with_discriminator.rs"
sha256: 2d4f5a069fdcb3057bb078d5e75b3de63cd477b97725e457079df24bd2c30600
- filename: "samples/server/petstore/rust-axum/output/openapi-v3/tests/oneof_untagged.rs"
- sha256: e72fbf81a9849dc7abb7e2169f2fc355c8b1cf991c0e2ffc083126abd9e966e7
+ sha256: 1d3fb01f65e98290b1d3eece28014c7d3e3f2fdf18e7110249d3c591cc4642ab
diff --git a/docs/generators/rust-axum.md b/docs/generators/rust-axum.md
index 743b58779eb..19d87abc124 100644
--- a/docs/generators/rust-axum.md
+++ b/docs/generators/rust-axum.md
@@ -77,6 +77,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
dyn
else
enum
+errors
extern
false
final
@@ -207,8 +208,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|Composite|✓|OAS2,OAS3
|Polymorphism|✗|OAS2,OAS3
|Union|✗|OAS3
-|allOf|✗|OAS2,OAS3
-|anyOf|✗|OAS3
+|allOf|✓|OAS2,OAS3
+|anyOf|✓|OAS3
|oneOf|✓|OAS3
|not|✗|OAS3
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustAxumServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustAxumServerCodegen.java
index 1c883774d85..1366797328a 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustAxumServerCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustAxumServerCodegen.java
@@ -99,6 +99,18 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
public RustAxumServerCodegen() {
super();
+ // The `#[validate(nested)]` macro relies on an internal field named `errors` to accumulate validation results. Therefore, defining a struct like this will fail:
+ //
+ // ```rust
+ // struct A {
+ // #[validate(nested)]
+ // errors: B,
+ // }
+ // ```
+ //
+ // To avoid this, either rename the field to something other than "errors", or reserve it.
+ this.reservedWords.add("errors");
+
modifyFeatureSet(features -> features
.wireFormatFeatures(EnumSet.of(
WireFormatFeature.JSON,
@@ -112,7 +124,9 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
.schemaSupportFeatures(EnumSet.of(
SchemaSupportFeature.Simple,
SchemaSupportFeature.Composite,
- SchemaSupportFeature.oneOf
+ SchemaSupportFeature.oneOf,
+ SchemaSupportFeature.anyOf,
+ SchemaSupportFeature.allOf
))
.excludeGlobalFeatures(
GlobalFeature.Info,
@@ -633,105 +647,163 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
return op;
}
- private void postProcessOneOfModels(List allModels) {
- final HashMap> oneOfMapDiscriminator = new HashMap<>();
+ private void postProcessPolymorphism(final List allModels) {
+ final HashMap> discriminatorsForModel = new HashMap<>();
- for (ModelMap mo : allModels) {
+ for (final ModelMap mo : allModels) {
final CodegenModel cm = mo.getModel();
final CodegenComposedSchemas cs = cm.getComposedSchemas();
if (cs != null) {
final List csOneOf = cs.getOneOf();
-
if (csOneOf != null) {
- for (CodegenProperty model : csOneOf) {
- // Generate a valid name for the enum variant.
- // Mainly needed for primitive types.
-
- model.datatypeWithEnum = camelize(model.dataType.replaceAll("(?:\\w+::)+(\\w+)", "$1")
- .replace("<", "Of").replace(">", ""));
-
- // Primitive type is not properly set, this overrides it to guarantee adequate model generation.
- if (!model.getDataType().matches(String.format(Locale.ROOT, ".*::%s", model.getDatatypeWithEnum()))) {
- model.isPrimitiveType = true;
- }
- }
-
+ processPolymorphismDataType(csOneOf);
cs.setOneOf(csOneOf);
cm.setComposedSchemas(cs);
}
+
+ final List csAnyOf = cs.getAnyOf();
+ if (csAnyOf != null) {
+ processPolymorphismDataType(csAnyOf);
+ cs.setAnyOf(csAnyOf);
+ cm.setComposedSchemas(cs);
+ }
}
if (cm.discriminator != null) {
- for (String model : cm.oneOf) {
- List discriminators = oneOfMapDiscriminator.getOrDefault(model, new ArrayList<>());
+ for (final String model : cm.oneOf) {
+ final List discriminators = discriminatorsForModel.getOrDefault(model, new ArrayList<>());
discriminators.add(cm.discriminator.getPropertyName());
- oneOfMapDiscriminator.put(model, discriminators);
+ discriminatorsForModel.put(model, discriminators);
+ }
+
+ for (final String model : cm.anyOf) {
+ final List discriminators = discriminatorsForModel.getOrDefault(model, new ArrayList<>());
+ discriminators.add(cm.discriminator.getPropertyName());
+ discriminatorsForModel.put(model, discriminators);
}
}
}
+ final var blocking = new HashSet();
for (ModelMap mo : allModels) {
final CodegenModel cm = mo.getModel();
- for (CodegenProperty var : cm.vars) {
- var.isDiscriminator = false;
+ final List discriminators = discriminatorsForModel.get(cm.getSchemaName());
+ if (discriminators != null) {
+ // If the discriminator field is not a defined attribute in the variant structure, create it.
+ if (!discriminating(discriminators, cm)) {
+ final String discriminator = discriminators.get(0);
+
+ CodegenProperty property = new CodegenProperty();
+
+ // Static attributes
+ // Only strings are supported by serde for tag field types, so it's the only one we'll deal with
+ property.openApiType = "string";
+ property.complexType = "string";
+ property.dataType = "String";
+ property.datatypeWithEnum = "String";
+ property.baseType = "string";
+ property.required = true;
+ property.isPrimitiveType = true;
+ property.isString = true;
+ property.isDiscriminator = true;
+
+ // Attributes based on the discriminator value
+ property.baseName = discriminator;
+ property.name = discriminator;
+ property.nameInCamelCase = camelize(discriminator);
+ property.nameInPascalCase = property.nameInCamelCase.substring(0, 1).toUpperCase(Locale.ROOT) + property.nameInCamelCase.substring(1);
+ property.nameInSnakeCase = underscore(discriminator).toUpperCase(Locale.ROOT);
+ property.getter = String.format(Locale.ROOT, "get%s", property.nameInPascalCase);
+ property.setter = String.format(Locale.ROOT, "set%s", property.nameInPascalCase);
+ property.defaultValueWithParam = String.format(Locale.ROOT, " = data.%s;", property.name);
+
+ // Attributes based on the model name
+ property.defaultValue = String.format(Locale.ROOT, "r#\"%s\"#.to_string()", cm.getSchemaName());
+ property.jsonSchema = String.format(Locale.ROOT, "{ \"default\":\"%s\"; \"type\":\"string\" }", cm.getSchemaName());
+
+ cm.vars.add(property);
+ }
}
- final List discriminatorsForModel = oneOfMapDiscriminator.get(cm.getSchemaName());
+ if (cm.vars.stream().noneMatch(v -> v.isDiscriminator)) {
+ blocking.add(cm.getSchemaName());
+ }
+ }
- if (discriminatorsForModel != null) {
- for (String discriminator : discriminatorsForModel) {
- boolean hasDiscriminatorDefined = false;
-
- for (CodegenProperty var : cm.vars) {
- if (var.baseName.equals(discriminator)) {
- var.isDiscriminator = true;
- hasDiscriminatorDefined = true;
- break;
- }
- }
-
- // If the discriminator field is not a defined attribute in the variant structure, create it.
- if (!hasDiscriminatorDefined) {
- CodegenProperty property = new CodegenProperty();
-
- // Static attributes
- // Only strings are supported by serde for tag field types, so it's the only one we'll deal with
- property.openApiType = "string";
- property.complexType = "string";
- property.dataType = "String";
- property.datatypeWithEnum = "String";
- property.baseType = "string";
- property.required = true;
- property.isPrimitiveType = true;
- property.isString = true;
- property.isDiscriminator = true;
-
- // Attributes based on the discriminator value
- property.baseName = discriminator;
- property.name = discriminator;
- property.nameInCamelCase = camelize(discriminator);
- property.nameInPascalCase = property.nameInCamelCase.substring(0, 1).toUpperCase(Locale.ROOT) + property.nameInCamelCase.substring(1);
- property.nameInSnakeCase = underscore(discriminator).toUpperCase(Locale.ROOT);
- property.getter = String.format(Locale.ROOT, "get%s", property.nameInPascalCase);
- property.setter = String.format(Locale.ROOT, "set%s", property.nameInPascalCase);
- property.defaultValueWithParam = String.format(Locale.ROOT, " = data.%s;", property.name);
-
- // Attributes based on the model name
- property.defaultValue = String.format(Locale.ROOT, "r#\"%s\"#.to_string()", cm.getSchemaName());
- property.jsonSchema = String.format(Locale.ROOT, "{ \"default\":\"%s\"; \"type\":\"string\" }", cm.getSchemaName());
-
- cm.vars.add(property);
- }
+ for (final ModelMap mo : allModels) {
+ final CodegenModel cm = mo.getModel();
+ if (cm.discriminator != null) {
+ // if no discriminator in any of variant -> disable discriminator
+ if (cm.oneOf.stream().anyMatch(blocking::contains) || cm.anyOf.stream().anyMatch(blocking::contains)) {
+ cm.discriminator = null;
}
}
}
}
+ private static boolean discriminating(final List discriminatorsForModel, final CodegenModel cm) {
+ resetDiscriminatorProperty(cm);
+
+ // Discriminator will be presented as enum tag -> One and only one tag is allowed
+ int countString = 0;
+ int countNonString = 0;
+ for (final CodegenProperty var : cm.vars) {
+ if (discriminatorsForModel.stream().anyMatch(discriminator -> var.baseName.equals(discriminator) || var.name.equals(discriminator))) {
+ if (var.isString) {
+ var.isDiscriminator = true;
+ ++countString;
+ } else
+ ++countNonString;
+ }
+ }
+
+ if (countString > 0 && (countNonString > 0 || countString > 1)) {
+ // at least two discriminator, one of them is string -> should not render serde tag
+ resetDiscriminatorProperty(cm);
+ }
+
+ return countNonString > 0 || countString > 0;
+ }
+
+ private static void resetDiscriminatorProperty(final CodegenModel cm) {
+ for (final CodegenProperty var : cm.vars) {
+ var.isDiscriminator = false;
+ }
+ }
+
+ private static void processPolymorphismDataType(final List cp) {
+ final HashSet dedupDataTypeWithEnum = new HashSet<>();
+ final HashMap dedupDataType = new HashMap<>();
+
+ int idx = 0;
+ for (CodegenProperty model : cp) {
+ // Generate a valid name for the enum variant.
+ // Mainly needed for primitive types.
+ model.datatypeWithEnum = camelize(model.dataType.replaceAll("(?:\\w+::)+(\\w+)", "$1")
+ .replace("<", "Of").replace(">", "")).replace(" ", "").replace(",", "");
+ if (!dedupDataTypeWithEnum.add(model.datatypeWithEnum)) {
+ model.datatypeWithEnum += ++idx;
+ }
+
+ dedupDataType.put(model.getDataType(), dedupDataType.getOrDefault(model.getDataType(), 0) + 1);
+
+ if (!model.getDataType().matches(String.format(Locale.ROOT, ".*::%s", model.getDatatypeWithEnum()))) {
+ model.isPrimitiveType = true;
+ }
+ }
+
+ for (CodegenProperty model : cp) {
+ if (dedupDataType.get(model.getDataType()) == 1) {
+ model.vendorExtensions.put("x-from-trait", true);
+ }
+ }
+ }
+
@Override
public OperationsMap postProcessOperationsWithModels(final OperationsMap operationsMap, List allModels) {
- postProcessOneOfModels(allModels);
+ postProcessPolymorphism(allModels);
final OperationMap operations = operationsMap.getOperations();
operations.put("classnamePascalCase", camelize(operations.getClassname()));
@@ -901,7 +973,7 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
// restore things to sensible values.
@Override
public CodegenParameter fromRequestBody(RequestBody body, Set imports, String bodyParameterName) {
- final Schema original_schema = ModelUtils.getSchemaFromRequestBody(body);
+ final var original_schema = ModelUtils.getSchemaFromRequestBody(body);
CodegenParameter codegenParameter = super.fromRequestBody(body, imports, bodyParameterName);
if (StringUtils.isNotBlank(original_schema.get$ref())) {
@@ -920,10 +992,10 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
@Override
public String toInstantiationType(final Schema p) {
if (ModelUtils.isArraySchema(p)) {
- final Schema inner = ModelUtils.getSchemaItems(p);
+ final var inner = ModelUtils.getSchemaItems(p);
return instantiationTypes.get("array") + "<" + getSchemaType(inner) + ">";
} else if (ModelUtils.isMapSchema(p)) {
- final Schema inner = ModelUtils.getAdditionalProperties(p);
+ final var inner = ModelUtils.getAdditionalProperties(p);
return instantiationTypes.get("map") + "<" + typeMapping.get("string") + ", " + getSchemaType(inner) + ">";
} else {
return null;
@@ -952,6 +1024,10 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
@Override
public String toDefaultValue(final Schema p) {
String defaultValue = null;
+
+ if (ModelUtils.isEnumSchema(p))
+ return null;
+
if ((ModelUtils.isNullable(p)) && (p.getDefault() != null) && ("null".equalsIgnoreCase(p.getDefault().toString())))
return "Nullable::Null";
@@ -965,6 +1041,9 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
} else if (ModelUtils.isNumberSchema(p)) {
if (p.getDefault() != null) {
defaultValue = p.getDefault().toString();
+ if (!defaultValue.contains(".")) {
+ defaultValue += ".0";
+ }
}
} else if (ModelUtils.isIntegerSchema(p)) {
if (p.getDefault() != null) {
@@ -1081,7 +1160,7 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
String cmd = System.getenv("RUST_POST_PROCESS_FILE");
if (StringUtils.isEmpty(cmd)) {
cmd = "rustfmt";
- command = new String[]{cmd, "--edition", "2021", fileName};
+ command = new String[]{cmd, "--edition", "2024", fileName};
} else {
command = new String[]{cmd, fileName};
}
@@ -1093,7 +1172,7 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
}
@Override
- protected void updateParameterForString(CodegenParameter codegenParameter, Schema parameterSchema) {
+ protected void updateParameterForString(CodegenParameter codegenParameter, final Schema parameterSchema) {
if (ModelUtils.isEmailSchema(parameterSchema)) {
codegenParameter.isEmail = true;
} else if (ModelUtils.isUUIDSchema(parameterSchema)) {
@@ -1120,7 +1199,7 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
codegenParameter.isDecimal = true;
codegenParameter.isPrimitiveType = true;
}
- if (Boolean.TRUE.equals(codegenParameter.isString)) {
+ if (codegenParameter.isString) {
codegenParameter.isPrimitiveType = true;
}
}
@@ -1152,6 +1231,16 @@ public class RustAxumServerCodegen extends AbstractRustCodegen implements Codege
return null;
}
+ @Override
+ public String toVarName(String name) {
+ final var varName = super.toVarName(name);
+
+ if (varName.startsWith("r#"))
+ return "r_" + varName.substring(2);
+
+ return varName;
+ }
+
static class PathMethodOperations {
public String path;
public ArrayList methodOperations;
diff --git a/modules/openapi-generator/src/main/resources/rust-axum/models.mustache b/modules/openapi-generator/src/main/resources/rust-axum/models.mustache
index 681d374d85a..6fedb142042 100644
--- a/modules/openapi-generator/src/main/resources/rust-axum/models.mustache
+++ b/modules/openapi-generator/src/main/resources/rust-axum/models.mustache
@@ -524,7 +524,7 @@ pub fn check_xss_map(v: &std::collections::HashMap) -> std::result
/// Enumeration of values.
/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]`
/// which helps with FFI.
-#[allow(non_camel_case_types)]
+#[allow(non_camel_case_types, clippy::large_enum_variant)]
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "conversion", derive(frunk_enum_derive::LabelledGenericEnum))]
@@ -751,17 +751,38 @@ impl std::str::FromStr for {{{classname}}} {
{{^arrayModelType}}
{{! general struct}}
{{#anyOf.size}}
-/// Any of:
-{{#anyOf}}
-/// - {{{.}}}
-{{/anyOf}}
-#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
-pub struct {{{classname}}}(Box);
+{{#discriminator}}
+#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
+#[serde(tag = "{{{propertyBaseName}}}")]
+{{/discriminator}}
+{{^discriminator}}
+#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
+#[serde(untagged)]
+{{/discriminator}}
+#[allow(non_camel_case_types, clippy::large_enum_variant)]
+pub enum {{{classname}}} {
+ {{#composedSchemas}}
+ {{#anyOf}}
+ {{{datatypeWithEnum}}}({{{dataType}}}),
+ {{/anyOf}}
+ {{/composedSchemas}}
+}
impl validator::Validate for {{{classname}}}
{
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
- std::result::Result::Ok(())
+ match self {
+ {{#composedSchemas}}
+ {{#anyOf}}
+ {{^isModel}}
+ Self::{{{datatypeWithEnum}}}(_) => std::result::Result::Ok(()),
+ {{/isModel}}
+ {{#isModel}}
+ Self::{{{datatypeWithEnum}}}(v) => v.validate(),
+ {{/isModel}}
+ {{/anyOf}}
+ {{/composedSchemas}}
+ }
}
}
@@ -776,11 +797,32 @@ impl std::str::FromStr for {{{classname}}} {
}
}
-impl PartialEq for {{{classname}}} {
- fn eq(&self, other: &Self) -> bool {
- self.0.get() == other.0.get()
+{{#discriminator}}
+impl serde::Serialize for {{{classname}}} {
+ fn serialize(&self, serializer: S) -> Result
+ where S: serde::Serializer {
+ match self {
+ {{#composedSchemas}}
+ {{#anyOf}}
+ Self::{{{datatypeWithEnum}}}(x) => x.serialize(serializer),
+ {{/anyOf}}
+ {{/composedSchemas}}
+ }
}
}
+{{/discriminator}}
+
+{{#composedSchemas}}
+{{#anyOf}}
+{{#vendorExtensions.x-from-trait}}
+impl From<{{{dataType}}}> for {{{classname}}} {
+ fn from(value: {{{dataType}}}) -> Self {
+ Self::{{{datatypeWithEnum}}}(value)
+ }
+}
+{{/vendorExtensions.x-from-trait}}
+{{/anyOf}}
+{{/composedSchemas}}
{{/anyOf.size}}
{{#oneOf.size}}
@@ -792,11 +834,11 @@ impl PartialEq for {{{classname}}} {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
{{/discriminator}}
-#[allow(non_camel_case_types)]
+#[allow(non_camel_case_types, clippy::large_enum_variant)]
pub enum {{{classname}}} {
{{#composedSchemas}}
{{#oneOf}}
- {{{datatypeWithEnum}}}(Box<{{{dataType}}}>),
+ {{{datatypeWithEnum}}}({{{dataType}}}),
{{/oneOf}}
{{/composedSchemas}}
}
@@ -807,18 +849,29 @@ impl validator::Validate for {{{classname}}}
match self {
{{#composedSchemas}}
{{#oneOf}}
- {{#isPrimitiveType}}
+ {{^isModel}}
Self::{{{datatypeWithEnum}}}(_) => std::result::Result::Ok(()),
- {{/isPrimitiveType}}
- {{^isPrimitiveType}}
- Self::{{{datatypeWithEnum}}}(x) => x.validate(),
- {{/isPrimitiveType}}
+ {{/isModel}}
+ {{#isModel}}
+ Self::{{{datatypeWithEnum}}}(v) => v.validate(),
+ {{/isModel}}
{{/oneOf}}
{{/composedSchemas}}
}
}
}
+/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
+/// as specified in https://swagger.io/docs/specification/serialization/
+/// Should be implemented in a serde deserializer
+impl std::str::FromStr for {{{classname}}} {
+ type Err = serde_json::Error;
+
+ fn from_str(s: &str) -> std::result::Result {
+ serde_json::from_str(s)
+ }
+}
+
{{#discriminator}}
impl serde::Serialize for {{{classname}}} {
fn serialize(&self, serializer: S) -> Result
@@ -834,29 +887,18 @@ impl serde::Serialize for {{{classname}}} {
}
{{/discriminator}}
-
-
{{#composedSchemas}}
{{#oneOf}}
+{{#vendorExtensions.x-from-trait}}
impl From<{{{dataType}}}> for {{{classname}}} {
fn from(value: {{{dataType}}}) -> Self {
- Self::{{{datatypeWithEnum}}}(Box::new(value))
+ Self::{{{datatypeWithEnum}}}(value)
}
}
+{{/vendorExtensions.x-from-trait}}
{{/oneOf}}
{{/composedSchemas}}
-/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
-/// as specified in https://swagger.io/docs/specification/serialization/
-/// Should be implemented in a serde deserializer
-impl std::str::FromStr for {{{classname}}} {
- type Err = serde_json::Error;
-
- fn from_str(s: &str) -> std::result::Result {
- serde_json::from_str(s)
- }
-}
-
{{/oneOf.size}}
{{^anyOf.size}}
{{^oneOf.size}}
@@ -871,8 +913,10 @@ pub struct {{{classname}}} {
/// Note: inline enums are not fully supported by openapi-generator
{{/isEnum}}
{{#isDiscriminator}}
+{{#isString}}
#[serde(default = "{{{classname}}}::_name_for_{{{name}}}")]
#[serde(serialize_with = "{{{classname}}}::_serialize_{{{name}}}")]
+{{/isString}}
{{/isDiscriminator}}
#[serde(rename = "{{{baseName}}}")]
{{#hasValidation}}
@@ -989,9 +1033,9 @@ pub struct {{{classname}}} {
{{/vars}}
}
-
{{#vars}}
{{#isDiscriminator}}
+{{#isString}}
impl {{{classname}}} {
fn _name_for_{{{name}}}() -> String {
String::from("{{{classname}}}")
@@ -1004,10 +1048,10 @@ impl {{{classname}}} {
s.serialize_str(&Self::_name_for_{{{name}}}())
}
}
+{{/isString}}
{{/isDiscriminator}}
{{/vars}}
-
{{#vars}}
{{#hasValidation}}
{{#pattern}}
@@ -1035,9 +1079,9 @@ fn validate_byte_{{#lambda.lowercase}}{{{classname}}}_{{{name}}}{{/lambda.lowerc
impl {{{classname}}} {
#[allow(clippy::new_without_default, clippy::too_many_arguments)]
- pub fn new({{#vars}}{{^defaultValue}}{{{name}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, {{/defaultValue}}{{/vars}}) -> {{{classname}}} {
+ pub fn new({{#vars}}{{^isDiscriminator}}{{^defaultValue}}{{{name}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, {{/defaultValue}}{{/isDiscriminator}}{{#isDiscriminator}}{{^isString}}{{^defaultValue}}{{{name}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, {{/defaultValue}}{{/isString}}{{/isDiscriminator}}{{/vars}}) -> {{{classname}}} {
{{{classname}}} {
-{{#vars}} {{#defaultValue}}{{{name}}}: {{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}{{{name}}}{{/defaultValue}},
+{{#vars}} {{^isDiscriminator}}{{#defaultValue}}{{{name}}}: {{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}{{{name}}}{{/defaultValue}}{{/isDiscriminator}}{{#isDiscriminator}}{{#isString}}{{{name}}}: Self::_name_for_{{{name}}}(){{/isString}}{{^isString}}{{#defaultValue}}{{{name}}}: {{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}{{{name}}}{{/defaultValue}}{{/isString}}{{/isDiscriminator}},
{{/vars}}
}
}
@@ -1075,7 +1119,7 @@ impl std::fmt::Display for {{{classname}}} {
{{/isArray}}
{{#isArray}}
{{#isNullable}}
- Some(self.{{{name}}}.as_ref().map_or(vec!["null".to_string()], |x| x.iter().map(|x| x.to_string()).collect::>().join(","))),
+ Some(self.{{{name}}}.as_ref().map_or("null".to_string(), |x| x.iter().map(|x| x.to_string()).collect::>().join(","))),
{{/isNullable}}
{{^isNullable}}
Some(self.{{{name}}}.iter().map(|x| x.to_string()).collect::>().join(",")),
@@ -1226,7 +1270,6 @@ impl std::convert::TryFrom for header::IntoHeaderValue<{{{classname
}
}
}
-
{{/oneOf.size}}
{{/anyOf.size}}
diff --git a/samples/server/petstore/rust-axum/output/apikey-authorization/src/models.rs b/samples/server/petstore/rust-axum/output/apikey-authorization/src/models.rs
index b20237912cd..5d7b8b641a2 100644
--- a/samples/server/petstore/rust-axum/output/apikey-authorization/src/models.rs
+++ b/samples/server/petstore/rust-axum/output/apikey-authorization/src/models.rs
@@ -594,7 +594,7 @@ pub struct PaymentMethod {
#[serde(rename = "type")]
#[validate(custom(function = "check_xss_string"))]
#[serde(skip_serializing_if = "Option::is_none")]
- pub r#type: Option,
+ pub r_type: Option,
}
impl PaymentMethod {
@@ -602,7 +602,7 @@ impl PaymentMethod {
pub fn new() -> PaymentMethod {
PaymentMethod {
name: None,
- r#type: None,
+ r_type: None,
}
}
}
@@ -616,9 +616,9 @@ impl std::fmt::Display for PaymentMethod {
self.name
.as_ref()
.map(|name| ["name".to_string(), name.to_string()].join(",")),
- self.r#type
+ self.r_type
.as_ref()
- .map(|r#type| ["type".to_string(), r#type.to_string()].join(",")),
+ .map(|r_type| ["type".to_string(), r_type.to_string()].join(",")),
];
write!(
@@ -641,7 +641,7 @@ impl std::str::FromStr for PaymentMethod {
#[allow(dead_code)]
struct IntermediateRep {
pub name: Vec,
- pub r#type: Vec,
+ pub r_type: Vec,
}
let mut intermediate_rep = IntermediateRep::default();
@@ -668,7 +668,7 @@ impl std::str::FromStr for PaymentMethod {
::from_str(val).map_err(|x| x.to_string())?,
),
#[allow(clippy::redundant_clone)]
- "type" => intermediate_rep.r#type.push(
+ "type" => intermediate_rep.r_type.push(
::from_str(val).map_err(|x| x.to_string())?,
),
_ => {
@@ -686,7 +686,7 @@ impl std::str::FromStr for PaymentMethod {
// Use the intermediate representation to return the struct
std::result::Result::Ok(PaymentMethod {
name: intermediate_rep.name.into_iter().next(),
- r#type: intermediate_rep.r#type.into_iter().next(),
+ r_type: intermediate_rep.r_type.into_iter().next(),
})
}
}
diff --git a/samples/server/petstore/rust-axum/output/apikey-auths/src/models.rs b/samples/server/petstore/rust-axum/output/apikey-auths/src/models.rs
index b20237912cd..5d7b8b641a2 100644
--- a/samples/server/petstore/rust-axum/output/apikey-auths/src/models.rs
+++ b/samples/server/petstore/rust-axum/output/apikey-auths/src/models.rs
@@ -594,7 +594,7 @@ pub struct PaymentMethod {
#[serde(rename = "type")]
#[validate(custom(function = "check_xss_string"))]
#[serde(skip_serializing_if = "Option::is_none")]
- pub r#type: Option,
+ pub r_type: Option,
}
impl PaymentMethod {
@@ -602,7 +602,7 @@ impl PaymentMethod {
pub fn new() -> PaymentMethod {
PaymentMethod {
name: None,
- r#type: None,
+ r_type: None,
}
}
}
@@ -616,9 +616,9 @@ impl std::fmt::Display for PaymentMethod {
self.name
.as_ref()
.map(|name| ["name".to_string(), name.to_string()].join(",")),
- self.r#type
+ self.r_type
.as_ref()
- .map(|r#type| ["type".to_string(), r#type.to_string()].join(",")),
+ .map(|r_type| ["type".to_string(), r_type.to_string()].join(",")),
];
write!(
@@ -641,7 +641,7 @@ impl std::str::FromStr for PaymentMethod {
#[allow(dead_code)]
struct IntermediateRep {
pub name: Vec,
- pub r#type: Vec,
+ pub r_type: Vec,
}
let mut intermediate_rep = IntermediateRep::default();
@@ -668,7 +668,7 @@ impl std::str::FromStr for PaymentMethod {
::from_str(val).map_err(|x| x.to_string())?,
),
#[allow(clippy::redundant_clone)]
- "type" => intermediate_rep.r#type.push(
+ "type" => intermediate_rep.r_type.push(
::from_str(val).map_err(|x| x.to_string())?,
),
_ => {
@@ -686,7 +686,7 @@ impl std::str::FromStr for PaymentMethod {
// Use the intermediate representation to return the struct
std::result::Result::Ok(PaymentMethod {
name: intermediate_rep.name.into_iter().next(),
- r#type: intermediate_rep.r#type.into_iter().next(),
+ r_type: intermediate_rep.r_type.into_iter().next(),
})
}
}
diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/src/models.rs b/samples/server/petstore/rust-axum/output/openapi-v3/src/models.rs
index 4d2fb31b5ab..a02749ce055 100644
--- a/samples/server/petstore/rust-axum/output/openapi-v3/src/models.rs
+++ b/samples/server/petstore/rust-axum/output/openapi-v3/src/models.rs
@@ -767,15 +767,20 @@ impl std::convert::TryFrom for header::IntoHeaderValue);
+#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
+#[serde(untagged)]
+#[allow(non_camel_case_types, clippy::large_enum_variant)]
+pub enum AnyOfGet202Response {
+ String(String),
+ Uuid(uuid::Uuid),
+}
impl validator::Validate for AnyOfGet202Response {
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
- std::result::Result::Ok(())
+ match self {
+ Self::String(_) => std::result::Result::Ok(()),
+ Self::Uuid(_) => std::result::Result::Ok(()),
+ }
}
}
@@ -790,22 +795,32 @@ impl std::str::FromStr for AnyOfGet202Response {
}
}
-impl PartialEq for AnyOfGet202Response {
- fn eq(&self, other: &Self) -> bool {
- self.0.get() == other.0.get()
+impl From for AnyOfGet202Response {
+ fn from(value: String) -> Self {
+ Self::String(value)
+ }
+}
+impl From for AnyOfGet202Response {
+ fn from(value: uuid::Uuid) -> Self {
+ Self::Uuid(value)
}
}
/// Test a model containing an anyOf of a hash map
-/// Any of:
-/// - String
-/// - std::collections::HashMap
-#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
-pub struct AnyOfHashMapObject(Box);
+#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
+#[serde(untagged)]
+#[allow(non_camel_case_types, clippy::large_enum_variant)]
+pub enum AnyOfHashMapObject {
+ String(String),
+ HashMapOfStringString(std::collections::HashMap),
+}
impl validator::Validate for AnyOfHashMapObject {
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
- std::result::Result::Ok(())
+ match self {
+ Self::String(_) => std::result::Result::Ok(()),
+ Self::HashMapOfStringString(_) => std::result::Result::Ok(()),
+ }
}
}
@@ -820,21 +835,32 @@ impl std::str::FromStr for AnyOfHashMapObject {
}
}
-impl PartialEq for AnyOfHashMapObject {
- fn eq(&self, other: &Self) -> bool {
- self.0.get() == other.0.get()
+impl From for AnyOfHashMapObject {
+ fn from(value: String) -> Self {
+ Self::String(value)
+ }
+}
+impl From> for AnyOfHashMapObject {
+ fn from(value: std::collections::HashMap) -> Self {
+ Self::HashMapOfStringString(value)
}
}
/// Test a model containing an anyOf
-/// Any of:
-/// - String
-#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
-pub struct AnyOfObject(Box);
+#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
+#[serde(untagged)]
+#[allow(non_camel_case_types, clippy::large_enum_variant)]
+pub enum AnyOfObject {
+ String(String),
+ String1(String),
+}
impl validator::Validate for AnyOfObject {
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
- std::result::Result::Ok(())
+ match self {
+ Self::String(_) => std::result::Result::Ok(()),
+ Self::String1(_) => std::result::Result::Ok(()),
+ }
}
}
@@ -849,12 +875,6 @@ impl std::str::FromStr for AnyOfObject {
}
}
-impl PartialEq for AnyOfObject {
- fn eq(&self, other: &Self) -> bool {
- self.0.get() == other.0.get()
- }
-}
-
/// Test containing an anyOf object
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
@@ -1166,7 +1186,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue for header::IntoHeaderValue);
+#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
+#[serde(untagged)]
+#[allow(non_camel_case_types, clippy::large_enum_variant)]
+pub enum Model12345AnyOfObject {
+ String(String),
+ String1(String),
+}
impl validator::Validate for Model12345AnyOfObject {
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
- std::result::Result::Ok(())
+ match self {
+ Self::String(_) => std::result::Result::Ok(()),
+ Self::String1(_) => std::result::Result::Ok(()),
+ }
}
}
@@ -1473,12 +1499,6 @@ impl std::str::FromStr for Model12345AnyOfObject {
}
}
-impl PartialEq for Model12345AnyOfObject {
- fn eq(&self, other: &Self) -> bool {
- self.0.get() == other.0.get()
- }
-}
-
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct MultigetGet201Response {
@@ -2319,8 +2339,9 @@ pub struct ObjectParam {
pub required_param: bool,
#[serde(rename = "optionalParam")]
+ #[validate(range(min = 1u64, max = 10000000000000000000u64))]
#[serde(skip_serializing_if = "Option::is_none")]
- pub optional_param: Option,
+ pub optional_param: Option,
}
impl ObjectParam {
@@ -2366,7 +2387,7 @@ impl std::str::FromStr for ObjectParam {
#[allow(dead_code)]
struct IntermediateRep {
pub required_param: Vec,
- pub optional_param: Vec,
+ pub optional_param: Vec,
}
let mut intermediate_rep = IntermediateRep::default();
@@ -2394,7 +2415,7 @@ impl std::str::FromStr for ObjectParam {
),
#[allow(clippy::redundant_clone)]
"optionalParam" => intermediate_rep.optional_param.push(
- ::from_str(val).map_err(|x| x.to_string())?,
+ ::from_str(val).map_err(|x| x.to_string())?,
),
_ => {
return std::result::Result::Err(
@@ -2813,10 +2834,10 @@ impl std::ops::DerefMut for Ok {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
-#[allow(non_camel_case_types)]
+#[allow(non_camel_case_types, clippy::large_enum_variant)]
pub enum OneOfGet200Response {
- I32(Box),
- VecOfString(Box>),
+ I32(i32),
+ VecOfString(Vec),
}
impl validator::Validate for OneOfGet200Response {
@@ -2828,17 +2849,6 @@ impl validator::Validate for OneOfGet200Response {
}
}
-impl From for OneOfGet200Response {
- fn from(value: i32) -> Self {
- Self::I32(Box::new(value))
- }
-}
-impl From> for OneOfGet200Response {
- fn from(value: Vec) -> Self {
- Self::VecOfString(Box::new(value))
- }
-}
-
/// Converts Query Parameters representation (style=form, explode=false) to a OneOfGet200Response value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
@@ -2850,6 +2860,17 @@ impl std::str::FromStr for OneOfGet200Response {
}
}
+impl From for OneOfGet200Response {
+ fn from(value: i32) -> Self {
+ Self::I32(value)
+ }
+}
+impl From> for OneOfGet200Response {
+ fn from(value: Vec) -> Self {
+ Self::VecOfString(value)
+ }
+}
+
#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct OptionalObjectHeader(i32);
@@ -2971,7 +2992,7 @@ impl std::ops::DerefMut for Result {
/// Enumeration of values.
/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]`
/// which helps with FFI.
-#[allow(non_camel_case_types)]
+#[allow(non_camel_case_types, clippy::large_enum_variant)]
#[repr(C)]
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
diff --git a/samples/server/petstore/rust-axum/output/openapi-v3/tests/oneof_untagged.rs b/samples/server/petstore/rust-axum/output/openapi-v3/tests/oneof_untagged.rs
index 63c04df4913..40d18c33605 100644
--- a/samples/server/petstore/rust-axum/output/openapi-v3/tests/oneof_untagged.rs
+++ b/samples/server/petstore/rust-axum/output/openapi-v3/tests/oneof_untagged.rs
@@ -12,7 +12,7 @@ fn test_oneof_schema_untagged() {
let test2 = r#"{"value": ["foo", "bar"]}"#;
let test3 = Test {
- value: OneOfGet200Response::I32(123.into()),
+ value: OneOfGet200Response::I32(123),
};
let test4 = Test {
value: OneOfGet200Response::VecOfString(vec!["foo".to_string(), "bar".to_string()].into()),
diff --git a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/models.rs b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/models.rs
index 69f5dda3e2e..bb81873332e 100644
--- a/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/models.rs
+++ b/samples/server/petstore/rust-axum/output/petstore-with-fake-endpoints-models-for-testing/src/models.rs
@@ -349,6 +349,8 @@ impl std::convert::TryFrom for header::IntoHeaderValue,
}
+impl Animal {
+ fn _name_for_class_name() -> String {
+ String::from("Animal")
+ }
+
+ fn _serialize_class_name(_: &String, s: S) -> Result
+ where
+ S: serde::Serializer,
+ {
+ s.serialize_str(&Self::_name_for_class_name())
+ }
+}
+
impl Animal {
#[allow(clippy::new_without_default, clippy::too_many_arguments)]
- pub fn new(class_name: String) -> Animal {
+ pub fn new() -> Animal {
Animal {
- class_name,
+ class_name: Self::_name_for_class_name(),
color: Some(r#"red"#.to_string()),
}
}
@@ -649,7 +664,7 @@ pub struct ApiResponse {
#[serde(rename = "type")]
#[validate(custom(function = "check_xss_string"))]
#[serde(skip_serializing_if = "Option::is_none")]
- pub r#type: Option,
+ pub r_type: Option,
#[serde(rename = "message")]
#[validate(custom(function = "check_xss_string"))]
@@ -662,7 +677,7 @@ impl ApiResponse {
pub fn new() -> ApiResponse {
ApiResponse {
code: None,
- r#type: None,
+ r_type: None,
message: None,
}
}
@@ -677,9 +692,9 @@ impl std::fmt::Display for ApiResponse {
self.code
.as_ref()
.map(|code| ["code".to_string(), code.to_string()].join(",")),
- self.r#type
+ self.r_type
.as_ref()
- .map(|r#type| ["type".to_string(), r#type.to_string()].join(",")),
+ .map(|r_type| ["type".to_string(), r_type.to_string()].join(",")),
self.message
.as_ref()
.map(|message| ["message".to_string(), message.to_string()].join(",")),
@@ -705,7 +720,7 @@ impl std::str::FromStr for ApiResponse {
#[allow(dead_code)]
struct IntermediateRep {
pub code: Vec,
- pub r#type: Vec,
+ pub r_type: Vec,
pub message: Vec,
}
@@ -733,7 +748,7 @@ impl std::str::FromStr for ApiResponse {
::from_str(val).map_err(|x| x.to_string())?,
),
#[allow(clippy::redundant_clone)]
- "type" => intermediate_rep.r#type.push(
+ "type" => intermediate_rep.r_type.push(
::from_str(val).map_err(|x| x.to_string())?,
),
#[allow(clippy::redundant_clone)]
@@ -755,7 +770,7 @@ impl std::str::FromStr for ApiResponse {
// Use the intermediate representation to return the struct
std::result::Result::Ok(ApiResponse {
code: intermediate_rep.code.into_iter().next(),
- r#type: intermediate_rep.r#type.into_iter().next(),
+ r_type: intermediate_rep.r_type.into_iter().next(),
message: intermediate_rep.message.into_iter().next(),
})
}
@@ -2539,7 +2554,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue
/// Enumeration of values.
/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]`
/// which helps with FFI.
-#[allow(non_camel_case_types)]
+#[allow(non_camel_case_types, clippy::large_enum_variant)]
#[repr(C)]
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
@@ -3337,7 +3352,9 @@ impl std::str::FromStr for List {
let val = match string_iter.next() {
Some(x) => x,
None => {
- return std::result::Result::Err("Missing value while parsing List".to_string());
+ return std::result::Result::Err(
+ "Missing value while parsing List".to_string(),
+ );
}
};
@@ -3976,7 +3993,9 @@ impl std::str::FromStr for Name {
let val = match string_iter.next() {
Some(x) => x,
None => {
- return std::result::Result::Err("Missing value while parsing Name".to_string());
+ return std::result::Result::Err(
+ "Missing value while parsing Name".to_string(),
+ );
}
};
@@ -4786,7 +4805,7 @@ impl std::convert::TryFrom for header::IntoHeaderValue for header::IntoHeaderValue,
+ pub r_return: Option,
}
impl Return {
#[allow(clippy::new_without_default, clippy::too_many_arguments)]
pub fn new() -> Return {
- Return { r#return: None }
+ Return { r_return: None }
}
}
@@ -5299,9 +5318,9 @@ impl Return {
impl std::fmt::Display for Return {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let params: Vec