Skip to content

Commit 10ea8c6

Browse files
committed
Support AnyOf, AllOf
1 parent 5f647b8 commit 10ea8c6

File tree

22 files changed

+281
-601
lines changed

22 files changed

+281
-601
lines changed

docs/generators/rust-axum.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl
207207
|Composite|✓|OAS2,OAS3
208208
|Polymorphism|✗|OAS2,OAS3
209209
|Union|✗|OAS3
210-
|allOf||OAS2,OAS3
211-
|anyOf||OAS3
210+
|allOf||OAS2,OAS3
211+
|anyOf||OAS3
212212
|oneOf|✓|OAS3
213213
|not|✗|OAS3
214214

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

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ public RustAxumServerCodegen() {
112112
.schemaSupportFeatures(EnumSet.of(
113113
SchemaSupportFeature.Simple,
114114
SchemaSupportFeature.Composite,
115-
SchemaSupportFeature.oneOf
115+
SchemaSupportFeature.oneOf,
116+
SchemaSupportFeature.anyOf,
117+
SchemaSupportFeature.allOf
116118
))
117119
.excludeGlobalFeatures(
118120
GlobalFeature.Info,
@@ -633,40 +635,53 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
633635
return op;
634636
}
635637

636-
private void postProcessOneOfModels(List<ModelMap> allModels) {
637-
final HashMap<String, List<String>> oneOfMapDiscriminator = new HashMap<>();
638+
private void postProcessOneAllAnyOfModels(List<ModelMap> allModels) {
639+
final HashMap<String, List<String>> mapDiscriminator = new HashMap<>();
638640

639641
for (ModelMap mo : allModels) {
640642
final CodegenModel cm = mo.getModel();
641643

642644
final CodegenComposedSchemas cs = cm.getComposedSchemas();
643645
if (cs != null) {
644646
final List<CodegenProperty> csOneOf = cs.getOneOf();
645-
646647
if (csOneOf != null) {
647-
for (CodegenProperty model : csOneOf) {
648-
// Generate a valid name for the enum variant.
649-
// Mainly needed for primitive types.
650-
651-
model.datatypeWithEnum = camelize(model.dataType.replaceAll("(?:\\w+::)+(\\w+)", "$1")
652-
.replace("<", "Of").replace(">", ""));
648+
processOneAllAnyOfModelDataType(csOneOf);
649+
cs.setAnyOf(csOneOf);
650+
cm.setComposedSchemas(cs);
651+
}
653652

654-
// Primitive type is not properly set, this overrides it to guarantee adequate model generation.
655-
if (!model.getDataType().matches(String.format(Locale.ROOT, ".*::%s", model.getDatatypeWithEnum()))) {
656-
model.isPrimitiveType = true;
657-
}
658-
}
653+
final List<CodegenProperty> csAnyOf = cs.getAnyOf();
654+
if (csAnyOf != null) {
655+
processOneAllAnyOfModelDataType(csAnyOf);
656+
cs.setAnyOf(csAnyOf);
657+
cm.setComposedSchemas(cs);
658+
}
659659

660-
cs.setOneOf(csOneOf);
660+
final List<CodegenProperty> csAllOf = cs.getAllOf();
661+
if (csAllOf != null) {
662+
processOneAllAnyOfModelDataType(csAllOf);
663+
cs.setAllOf(csAllOf);
661664
cm.setComposedSchemas(cs);
662665
}
663666
}
664667

665668
if (cm.discriminator != null) {
666669
for (String model : cm.oneOf) {
667-
List<String> discriminators = oneOfMapDiscriminator.getOrDefault(model, new ArrayList<>());
670+
final List<String> discriminators = mapDiscriminator.getOrDefault(model, new ArrayList<>());
671+
discriminators.add(cm.discriminator.getPropertyName());
672+
mapDiscriminator.put(model, discriminators);
673+
}
674+
675+
for (String model : cm.anyOf) {
676+
final List<String> discriminators = mapDiscriminator.getOrDefault(model, new ArrayList<>());
677+
discriminators.add(cm.discriminator.getPropertyName());
678+
mapDiscriminator.put(model, discriminators);
679+
}
680+
681+
for (String model : cm.allOf) {
682+
final List<String> discriminators = mapDiscriminator.getOrDefault(model, new ArrayList<>());
668683
discriminators.add(cm.discriminator.getPropertyName());
669-
oneOfMapDiscriminator.put(model, discriminators);
684+
mapDiscriminator.put(model, discriminators);
670685
}
671686
}
672687
}
@@ -678,7 +693,7 @@ private void postProcessOneOfModels(List<ModelMap> allModels) {
678693
var.isDiscriminator = false;
679694
}
680695

681-
final List<String> discriminatorsForModel = oneOfMapDiscriminator.get(cm.getSchemaName());
696+
final List<String> discriminatorsForModel = mapDiscriminator.get(cm.getSchemaName());
682697

683698
if (discriminatorsForModel != null) {
684699
for (String discriminator : discriminatorsForModel) {
@@ -729,9 +744,34 @@ private void postProcessOneOfModels(List<ModelMap> allModels) {
729744
}
730745
}
731746

747+
private static void processOneAllAnyOfModelDataType(final List<CodegenProperty> cp) {
748+
final HashSet<String> dedup = new HashSet<>();
749+
750+
final List<CodegenProperty> toRemove = new ArrayList();
751+
for (CodegenProperty model : cp) {
752+
// Generate a valid name for the enum variant.
753+
// Mainly needed for primitive types.
754+
model.datatypeWithEnum = camelize(model.dataType.replaceAll("(?:\\w+::)+(\\w+)", "$1")
755+
.replace("<", "Of").replace(">", "")).replace(" ", "").replace(",", "");
756+
757+
if (!model.getDataType().matches(String.format(Locale.ROOT, ".*::%s", model.getDatatypeWithEnum()))) {
758+
model.isPrimitiveType = true;
759+
model.datatypeWithEnum += "Type";
760+
}
761+
762+
if (!dedup.add(model.datatypeWithEnum)) {
763+
toRemove.add(model);
764+
}
765+
}
766+
767+
for (var model : toRemove) {
768+
cp.remove(model);
769+
}
770+
}
771+
732772
@Override
733773
public OperationsMap postProcessOperationsWithModels(final OperationsMap operationsMap, List<ModelMap> allModels) {
734-
postProcessOneOfModels(allModels);
774+
postProcessOneAllAnyOfModels(allModels);
735775

736776
final OperationMap operations = operationsMap.getOperations();
737777
operations.put("classnamePascalCase", camelize(operations.getClassname()));

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ axum-extra = { version = "0.10", features = ["cookie", "query"] }
4646
base64 = "0.22"
4747
bytes = "1"
4848
chrono = { version = "0.4", features = ["serde"] }
49+
derive_more = { version = "2", features = ["from"] }
4950
frunk = { version = "0.4", optional = true }
5051
frunk-enum-core = { version = "0.3", optional = true }
5152
frunk-enum-derive = { version = "0.3", optional = true }

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

Lines changed: 101 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -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, derive_more::From)]
756+
#[serde(tag = "{{{propertyBaseName}}}")]
757+
{{/discriminator}}
758+
{{^discriminator}}
759+
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, derive_more::From)]
760+
#[serde(untagged)]
761+
{{/discriminator}}
762+
#[allow(non_camel_case_types)]
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,27 +797,21 @@ 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()
782-
}
783-
}
784-
785800
{{/anyOf.size}}
786801
{{#oneOf.size}}
787802
{{#discriminator}}
788-
#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
803+
#[derive(Debug, Clone, PartialEq, serde::Deserialize, derive_more::From)]
789804
#[serde(tag = "{{{propertyBaseName}}}")]
790805
{{/discriminator}}
791806
{{^discriminator}}
792-
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
807+
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, derive_more::From)]
793808
#[serde(untagged)]
794809
{{/discriminator}}
795810
#[allow(non_camel_case_types)]
796811
pub enum {{{classname}}} {
797812
{{#composedSchemas}}
798813
{{#oneOf}}
799-
{{{datatypeWithEnum}}}(Box<{{{dataType}}}>),
814+
{{{datatypeWithEnum}}}({{{dataType}}}),
800815
{{/oneOf}}
801816
{{/composedSchemas}}
802817
}
@@ -806,14 +821,14 @@ impl validator::Validate for {{{classname}}}
806821
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
807822
match self {
808823
{{#composedSchemas}}
809-
{{#oneOf}}
810-
{{#isPrimitiveType}}
824+
{{#anyOf}}
825+
{{^isModel}}
811826
Self::{{{datatypeWithEnum}}}(_) => std::result::Result::Ok(()),
812-
{{/isPrimitiveType}}
813-
{{^isPrimitiveType}}
814-
Self::{{{datatypeWithEnum}}}(x) => x.validate(),
815-
{{/isPrimitiveType}}
816-
{{/oneOf}}
827+
{{/isModel}}
828+
{{#isModel}}
829+
Self::{{{datatypeWithEnum}}}(v) => v.validate(),
830+
{{/isModel}}
831+
{{/anyOf}}
817832
{{/composedSchemas}}
818833
}
819834
}
@@ -834,17 +849,68 @@ impl serde::Serialize for {{{classname}}} {
834849
}
835850
{{/discriminator}}
836851
852+
/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
853+
/// as specified in https://swagger.io/docs/specification/serialization/
854+
/// Should be implemented in a serde deserializer
855+
impl std::str::FromStr for {{{classname}}} {
856+
type Err = serde_json::Error;
857+
858+
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
859+
serde_json::from_str(s)
860+
}
861+
}
862+
863+
{{/oneOf.size}}
864+
{{#allOf.size}}
865+
{{#discriminator}}
866+
#[derive(Debug, Clone, PartialEq, serde::Deserialize, derive_more::From)]
867+
#[serde(tag = "{{{propertyBaseName}}}")]
868+
{{/discriminator}}
869+
{{^discriminator}}
870+
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, derive_more::From)]
871+
#[serde(untagged)]
872+
{{/discriminator}}
873+
#[allow(non_camel_case_types)]
874+
pub enum {{{classname}}} {
875+
{{#composedSchemas}}
876+
{{#allOf}}
877+
{{{datatypeWithEnum}}}({{{dataType}}}),
878+
{{/allOf}}
879+
{{/composedSchemas}}
880+
}
837881
882+
impl validator::Validate for {{{classname}}}
883+
{
884+
fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
885+
match self {
886+
{{#composedSchemas}}
887+
{{#allOf}}
888+
{{^isModel}}
889+
Self::{{{datatypeWithEnum}}}(_) => std::result::Result::Ok(()),
890+
{{/isModel}}
891+
{{#isModel}}
892+
Self::{{{datatypeWithEnum}}}(v) => v.validate(),
893+
{{/isModel}}
894+
{{/allOf}}
895+
{{/composedSchemas}}
896+
}
897+
}
898+
}
838899
839-
{{#composedSchemas}}
840-
{{#oneOf}}
841-
impl From<{{{dataType}}}> for {{{classname}}} {
842-
fn from(value: {{{dataType}}}) -> Self {
843-
Self::{{{datatypeWithEnum}}}(Box::new(value))
900+
{{#discriminator}}
901+
impl serde::Serialize for {{{classname}}} {
902+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
903+
where S: serde::Serializer {
904+
match self {
905+
{{#composedSchemas}}
906+
{{#allOf}}
907+
Self::{{{datatypeWithEnum}}}(x) => x.serialize(serializer),
908+
{{/allOf}}
909+
{{/composedSchemas}}
910+
}
844911
}
845912
}
846-
{{/oneOf}}
847-
{{/composedSchemas}}
913+
{{/discriminator}}
848914
849915
/// Converts Query Parameters representation (style=form, explode=false) to a {{{classname}}} value
850916
/// as specified in https://swagger.io/docs/specification/serialization/
@@ -857,9 +923,10 @@ impl std::str::FromStr for {{{classname}}} {
857923
}
858924
}
859925
860-
{{/oneOf.size}}
926+
{{/allOf.size}}
861927
{{^anyOf.size}}
862928
{{^oneOf.size}}
929+
{{^allOf.size}}
863930
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
864931
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
865932
pub struct {{{classname}}} {
@@ -989,7 +1056,6 @@ pub struct {{{classname}}} {
9891056
{{/vars}}
9901057
}
9911058
992-
9931059
{{#vars}}
9941060
{{#isDiscriminator}}
9951061
impl {{{classname}}} {
@@ -1007,7 +1073,6 @@ impl {{{classname}}} {
10071073
{{/isDiscriminator}}
10081074
{{/vars}}
10091075
1010-
10111076
{{#vars}}
10121077
{{#hasValidation}}
10131078
{{#pattern}}
@@ -1189,12 +1254,14 @@ impl std::str::FromStr for {{{classname}}} {
11891254
})
11901255
}
11911256
}
1257+
{{/allOf.size}}
11921258
{{/oneOf.size}}
11931259
{{/anyOf.size}}
11941260
{{/arrayModelType}}
11951261

11961262
{{^anyOf.size}}
11971263
{{^oneOf.size}}
1264+
{{^allOf.size}}
11981265
// Methods for converting between header::IntoHeaderValue<{{{classname}}}> and HeaderValue
11991266

12001267
#[cfg(feature = "server")]
@@ -1226,7 +1293,7 @@ impl std::convert::TryFrom<HeaderValue> for header::IntoHeaderValue<{{{classname
12261293
}
12271294
}
12281295
}
1229-
1296+
{{/allOf.size}}
12301297
{{/oneOf.size}}
12311298
{{/anyOf.size}}
12321299

samples/server/petstore/rust-axum/output/apikey-authorization/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ axum-extra = { version = "0.10", features = ["cookie", "query"] }
2525
base64 = "0.22"
2626
bytes = "1"
2727
chrono = { version = "0.4", features = ["serde"] }
28+
derive_more = { version = "2", features = ["from"] }
2829
frunk = { version = "0.4", optional = true }
2930
frunk-enum-core = { version = "0.3", optional = true }
3031
frunk-enum-derive = { version = "0.3", optional = true }

samples/server/petstore/rust-axum/output/apikey-auths/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ axum-extra = { version = "0.10", features = ["cookie", "query"] }
2525
base64 = "0.22"
2626
bytes = "1"
2727
chrono = { version = "0.4", features = ["serde"] }
28+
derive_more = { version = "2", features = ["from"] }
2829
frunk = { version = "0.4", optional = true }
2930
frunk-enum-core = { version = "0.3", optional = true }
3031
frunk-enum-derive = { version = "0.3", optional = true }

samples/server/petstore/rust-axum/output/multipart-v3/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ axum-extra = { version = "0.10", features = ["cookie", "query"] }
2525
base64 = "0.22"
2626
bytes = "1"
2727
chrono = { version = "0.4", features = ["serde"] }
28+
derive_more = { version = "2", features = ["from"] }
2829
frunk = { version = "0.4", optional = true }
2930
frunk-enum-core = { version = "0.3", optional = true }
3031
frunk-enum-derive = { version = "0.3", optional = true }

0 commit comments

Comments
 (0)