Skip to content

Commit e38f6c0

Browse files
authored
[Rust-Axum] Support AnyOf, AllOf (#21948)
* Support AnyOf, AllOf * Update * Fix * Update * Update * Update * Update * Update * Update * Update * Update * Update * Update * Update * Update
1 parent 2afac1a commit e38f6c0

File tree

11 files changed

+442
-262
lines changed

11 files changed

+442
-262
lines changed

bin/utils/test_file_list.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,4 @@
6161
- filename: "samples/server/petstore/rust-axum/output/rust-axum-oneof/tests/oneof_with_discriminator.rs"
6262
sha256: 2d4f5a069fdcb3057bb078d5e75b3de63cd477b97725e457079df24bd2c30600
6363
- filename: "samples/server/petstore/rust-axum/output/openapi-v3/tests/oneof_untagged.rs"
64-
sha256: e72fbf81a9849dc7abb7e2169f2fc355c8b1cf991c0e2ffc083126abd9e966e7
64+
sha256: 1d3fb01f65e98290b1d3eece28014c7d3e3f2fdf18e7110249d3c591cc4642ab

docs/generators/rust-axum.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
7777
<li>dyn</li>
7878
<li>else</li>
7979
<li>enum</li>
80+
<li>errors</li>
8081
<li>extern</li>
8182
<li>false</li>
8283
<li>final</li>
@@ -207,8 +208,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl
207208
|Composite|✓|OAS2,OAS3
208209
|Polymorphism|✗|OAS2,OAS3
209210
|Union|✗|OAS3
210-
|allOf||OAS2,OAS3
211-
|anyOf||OAS3
211+
|allOf||OAS2,OAS3
212+
|anyOf||OAS3
212213
|oneOf|✓|OAS3
213214
|not|✗|OAS3
214215

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

Lines changed: 163 additions & 74 deletions
Large diffs are not rendered by default.

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

Lines changed: 81 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ pub fn check_xss_map<T>(v: &std::collections::HashMap<String, T>) -> std::result
524524
/// Enumeration of values.
525525
/// Since this enum's variants do not hold data, we can easily define them as `#[repr(C)]`
526526
/// which helps with FFI.
527-
#[allow(non_camel_case_types)]
527+
#[allow(non_camel_case_types, clippy::large_enum_variant)]
528528
#[repr(C)]
529529
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
530530
#[cfg_attr(feature = "conversion", derive(frunk_enum_derive::LabelledGenericEnum))]
@@ -751,17 +751,38 @@ impl std::str::FromStr for {{{classname}}} {
751751
{{^arrayModelType}}
752752
{{! general struct}}
753753
{{#anyOf.size}}
754-
/// Any of:
755-
{{#anyOf}}
756-
/// - {{{.}}}
757-
{{/anyOf}}
758-
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
759-
pub struct {{{classname}}}(Box<serde_json::value::RawValue>);
754+
{{#discriminator}}
755+
#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
756+
#[serde(tag = "{{{propertyBaseName}}}")]
757+
{{/discriminator}}
758+
{{^discriminator}}
759+
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
760+
#[serde(untagged)]
761+
{{/discriminator}}
762+
#[allow(non_camel_case_types, clippy::large_enum_variant)]
763+
pub enum {{{classname}}} {
764+
{{#composedSchemas}}
765+
{{#anyOf}}
766+
{{{datatypeWithEnum}}}({{{dataType}}}),
767+
{{/anyOf}}
768+
{{/composedSchemas}}
769+
}
760770
761771
impl validator::Validate for {{{classname}}}
762772
{
763773
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
764-
std::result::Result::Ok(())
774+
match self {
775+
{{#composedSchemas}}
776+
{{#anyOf}}
777+
{{^isModel}}
778+
Self::{{{datatypeWithEnum}}}(_) => std::result::Result::Ok(()),
779+
{{/isModel}}
780+
{{#isModel}}
781+
Self::{{{datatypeWithEnum}}}(v) => v.validate(),
782+
{{/isModel}}
783+
{{/anyOf}}
784+
{{/composedSchemas}}
785+
}
765786
}
766787
}
767788
@@ -776,11 +797,32 @@ impl std::str::FromStr for {{{classname}}} {
776797
}
777798
}
778799
779-
impl PartialEq for {{{classname}}} {
780-
fn eq(&self, other: &Self) -> bool {
781-
self.0.get() == other.0.get()
800+
{{#discriminator}}
801+
impl serde::Serialize for {{{classname}}} {
802+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
803+
where S: serde::Serializer {
804+
match self {
805+
{{#composedSchemas}}
806+
{{#anyOf}}
807+
Self::{{{datatypeWithEnum}}}(x) => x.serialize(serializer),
808+
{{/anyOf}}
809+
{{/composedSchemas}}
810+
}
811+
}
812+
}
813+
{{/discriminator}}
814+
815+
{{#composedSchemas}}
816+
{{#anyOf}}
817+
{{#vendorExtensions.x-from-trait}}
818+
impl From<{{{dataType}}}> for {{{classname}}} {
819+
fn from(value: {{{dataType}}}) -> Self {
820+
Self::{{{datatypeWithEnum}}}(value)
782821
}
783822
}
823+
{{/vendorExtensions.x-from-trait}}
824+
{{/anyOf}}
825+
{{/composedSchemas}}
784826
785827
{{/anyOf.size}}
786828
{{#oneOf.size}}
@@ -792,11 +834,11 @@ impl PartialEq for {{{classname}}} {
792834
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
793835
#[serde(untagged)]
794836
{{/discriminator}}
795-
#[allow(non_camel_case_types)]
837+
#[allow(non_camel_case_types, clippy::large_enum_variant)]
796838
pub enum {{{classname}}} {
797839
{{#composedSchemas}}
798840
{{#oneOf}}
799-
{{{datatypeWithEnum}}}(Box<{{{dataType}}}>),
841+
{{{datatypeWithEnum}}}({{{dataType}}}),
800842
{{/oneOf}}
801843
{{/composedSchemas}}
802844
}
@@ -807,18 +849,29 @@ impl validator::Validate for {{{classname}}}
807849
match self {
808850
{{#composedSchemas}}
809851
{{#oneOf}}
810-
{{#isPrimitiveType}}
852+
{{^isModel}}
811853
Self::{{{datatypeWithEnum}}}(_) => std::result::Result::Ok(()),
812-
{{/isPrimitiveType}}
813-
{{^isPrimitiveType}}
814-
Self::{{{datatypeWithEnum}}}(x) => x.validate(),
815-
{{/isPrimitiveType}}
854+
{{/isModel}}
855+
{{#isModel}}
856+
Self::{{{datatypeWithEnum}}}(v) => v.validate(),
857+
{{/isModel}}
816858
{{/oneOf}}
817859
{{/composedSchemas}}
818860
}
819861
}
820862
}
821863
864+
/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
865+
/// as specified in https://swagger.io/docs/specification/serialization/
866+
/// Should be implemented in a serde deserializer
867+
impl std::str::FromStr for {{{classname}}} {
868+
type Err = serde_json::Error;
869+
870+
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
871+
serde_json::from_str(s)
872+
}
873+
}
874+
822875
{{#discriminator}}
823876
impl serde::Serialize for {{{classname}}} {
824877
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -834,29 +887,18 @@ impl serde::Serialize for {{{classname}}} {
834887
}
835888
{{/discriminator}}
836889
837-
838-
839890
{{#composedSchemas}}
840891
{{#oneOf}}
892+
{{#vendorExtensions.x-from-trait}}
841893
impl From<{{{dataType}}}> for {{{classname}}} {
842894
fn from(value: {{{dataType}}}) -> Self {
843-
Self::{{{datatypeWithEnum}}}(Box::new(value))
895+
Self::{{{datatypeWithEnum}}}(value)
844896
}
845897
}
898+
{{/vendorExtensions.x-from-trait}}
846899
{{/oneOf}}
847900
{{/composedSchemas}}
848901
849-
/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
850-
/// as specified in https://swagger.io/docs/specification/serialization/
851-
/// Should be implemented in a serde deserializer
852-
impl std::str::FromStr for {{{classname}}} {
853-
type Err = serde_json::Error;
854-
855-
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
856-
serde_json::from_str(s)
857-
}
858-
}
859-
860902
{{/oneOf.size}}
861903
{{^anyOf.size}}
862904
{{^oneOf.size}}
@@ -871,8 +913,10 @@ pub struct {{{classname}}} {
871913
/// Note: inline enums are not fully supported by openapi-generator
872914
{{/isEnum}}
873915
{{#isDiscriminator}}
916+
{{#isString}}
874917
#[serde(default = "{{{classname}}}::_name_for_{{{name}}}")]
875918
#[serde(serialize_with = "{{{classname}}}::_serialize_{{{name}}}")]
919+
{{/isString}}
876920
{{/isDiscriminator}}
877921
#[serde(rename = "{{{baseName}}}")]
878922
{{#hasValidation}}
@@ -989,9 +1033,9 @@ pub struct {{{classname}}} {
9891033
{{/vars}}
9901034
}
9911035
992-
9931036
{{#vars}}
9941037
{{#isDiscriminator}}
1038+
{{#isString}}
9951039
impl {{{classname}}} {
9961040
fn _name_for_{{{name}}}() -> String {
9971041
String::from("{{{classname}}}")
@@ -1004,10 +1048,10 @@ impl {{{classname}}} {
10041048
s.serialize_str(&Self::_name_for_{{{name}}}())
10051049
}
10061050
}
1051+
{{/isString}}
10071052
{{/isDiscriminator}}
10081053
{{/vars}}
10091054
1010-
10111055
{{#vars}}
10121056
{{#hasValidation}}
10131057
{{#pattern}}
@@ -1035,9 +1079,9 @@ fn validate_byte_{{#lambda.lowercase}}{{{classname}}}_{{{name}}}{{/lambda.lowerc
10351079
10361080
impl {{{classname}}} {
10371081
#[allow(clippy::new_without_default, clippy::too_many_arguments)]
1038-
pub fn new({{#vars}}{{^defaultValue}}{{{name}}}: {{#isNullable}}Nullable<{{/isNullable}}{{{dataType}}}{{#isNullable}}>{{/isNullable}}, {{/defaultValue}}{{/vars}}) -> {{{classname}}} {
1082+
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}}} {
10391083
{{{classname}}} {
1040-
{{#vars}} {{#defaultValue}}{{{name}}}: {{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}{{{name}}}{{/defaultValue}},
1084+
{{#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}},
10411085
{{/vars}}
10421086
}
10431087
}
@@ -1075,7 +1119,7 @@ impl std::fmt::Display for {{{classname}}} {
10751119
{{/isArray}}
10761120
{{#isArray}}
10771121
{{#isNullable}}
1078-
Some(self.{{{name}}}.as_ref().map_or(vec!["null".to_string()], |x| x.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(","))),
1122+
Some(self.{{{name}}}.as_ref().map_or("null".to_string(), |x| x.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(","))),
10791123
{{/isNullable}}
10801124
{{^isNullable}}
10811125
Some(self.{{{name}}}.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",")),
@@ -1226,7 +1270,6 @@ impl std::convert::TryFrom<HeaderValue> for header::IntoHeaderValue<{{{classname
12261270
}
12271271
}
12281272
}
1229-
12301273
{{/oneOf.size}}
12311274
{{/anyOf.size}}
12321275

samples/server/petstore/rust-axum/output/apikey-authorization/src/models.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -594,15 +594,15 @@ pub struct PaymentMethod {
594594
#[serde(rename = "type")]
595595
#[validate(custom(function = "check_xss_string"))]
596596
#[serde(skip_serializing_if = "Option::is_none")]
597-
pub r#type: Option<String>,
597+
pub r_type: Option<String>,
598598
}
599599

600600
impl PaymentMethod {
601601
#[allow(clippy::new_without_default, clippy::too_many_arguments)]
602602
pub fn new() -> PaymentMethod {
603603
PaymentMethod {
604604
name: None,
605-
r#type: None,
605+
r_type: None,
606606
}
607607
}
608608
}
@@ -616,9 +616,9 @@ impl std::fmt::Display for PaymentMethod {
616616
self.name
617617
.as_ref()
618618
.map(|name| ["name".to_string(), name.to_string()].join(",")),
619-
self.r#type
619+
self.r_type
620620
.as_ref()
621-
.map(|r#type| ["type".to_string(), r#type.to_string()].join(",")),
621+
.map(|r_type| ["type".to_string(), r_type.to_string()].join(",")),
622622
];
623623

624624
write!(
@@ -641,7 +641,7 @@ impl std::str::FromStr for PaymentMethod {
641641
#[allow(dead_code)]
642642
struct IntermediateRep {
643643
pub name: Vec<String>,
644-
pub r#type: Vec<String>,
644+
pub r_type: Vec<String>,
645645
}
646646

647647
let mut intermediate_rep = IntermediateRep::default();
@@ -668,7 +668,7 @@ impl std::str::FromStr for PaymentMethod {
668668
<String as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?,
669669
),
670670
#[allow(clippy::redundant_clone)]
671-
"type" => intermediate_rep.r#type.push(
671+
"type" => intermediate_rep.r_type.push(
672672
<String as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?,
673673
),
674674
_ => {
@@ -686,7 +686,7 @@ impl std::str::FromStr for PaymentMethod {
686686
// Use the intermediate representation to return the struct
687687
std::result::Result::Ok(PaymentMethod {
688688
name: intermediate_rep.name.into_iter().next(),
689-
r#type: intermediate_rep.r#type.into_iter().next(),
689+
r_type: intermediate_rep.r_type.into_iter().next(),
690690
})
691691
}
692692
}

samples/server/petstore/rust-axum/output/apikey-auths/src/models.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -594,15 +594,15 @@ pub struct PaymentMethod {
594594
#[serde(rename = "type")]
595595
#[validate(custom(function = "check_xss_string"))]
596596
#[serde(skip_serializing_if = "Option::is_none")]
597-
pub r#type: Option<String>,
597+
pub r_type: Option<String>,
598598
}
599599

600600
impl PaymentMethod {
601601
#[allow(clippy::new_without_default, clippy::too_many_arguments)]
602602
pub fn new() -> PaymentMethod {
603603
PaymentMethod {
604604
name: None,
605-
r#type: None,
605+
r_type: None,
606606
}
607607
}
608608
}
@@ -616,9 +616,9 @@ impl std::fmt::Display for PaymentMethod {
616616
self.name
617617
.as_ref()
618618
.map(|name| ["name".to_string(), name.to_string()].join(",")),
619-
self.r#type
619+
self.r_type
620620
.as_ref()
621-
.map(|r#type| ["type".to_string(), r#type.to_string()].join(",")),
621+
.map(|r_type| ["type".to_string(), r_type.to_string()].join(",")),
622622
];
623623

624624
write!(
@@ -641,7 +641,7 @@ impl std::str::FromStr for PaymentMethod {
641641
#[allow(dead_code)]
642642
struct IntermediateRep {
643643
pub name: Vec<String>,
644-
pub r#type: Vec<String>,
644+
pub r_type: Vec<String>,
645645
}
646646

647647
let mut intermediate_rep = IntermediateRep::default();
@@ -668,7 +668,7 @@ impl std::str::FromStr for PaymentMethod {
668668
<String as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?,
669669
),
670670
#[allow(clippy::redundant_clone)]
671-
"type" => intermediate_rep.r#type.push(
671+
"type" => intermediate_rep.r_type.push(
672672
<String as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?,
673673
),
674674
_ => {
@@ -686,7 +686,7 @@ impl std::str::FromStr for PaymentMethod {
686686
// Use the intermediate representation to return the struct
687687
std::result::Result::Ok(PaymentMethod {
688688
name: intermediate_rep.name.into_iter().next(),
689-
r#type: intermediate_rep.r#type.into_iter().next(),
689+
r_type: intermediate_rep.r_type.into_iter().next(),
690690
})
691691
}
692692
}

0 commit comments

Comments
 (0)