OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.96k stars 6.59k forks source link

[BUG] [Rust-Axum] Unable to use `oneOf` generated structures #20101

Open Victoria-Casasampere-BeeTheData opened 1 week ago

Victoria-Casasampere-BeeTheData commented 1 week ago

Bug Report Checklist

Description

The structure generated by models using oneOf cannot be easily or efficiently converted to the specific structure type.

Using the provided declaration, with the current implementation of oneOf and anyOf handling, generates a Message struct with a private field of the RawValue type, with 2 additional structures named Hello and Goodbye that have usable fields and traits. The generated endpoint Default/foo gets Message provided as one of its arguments, but this type is mostly useless. There is no enumeration or information available within the structure to facilitate obtaining the adequate model programmatically, with no guarantee that further modifications to the model (ones that add or remove fields to the list of variants) will have explicit handling within the user code. The only way to currently use the Message structure semi-reasonably is done by attempting to deserialize Hello and Goodbye from Message and seeing which one does not fail, or deserializing Value from Message, manually checking the tagged field, and generating the adequate type from there. Both of these options complicate the error handling and the use of the values, as they have separate types and cannot be assigned to the same variable.

openapi-generator version

7.10.0-SNAPSHOT

OpenAPI declaration file content or url

https://gist.github.com/Victoria-Casasampere-BeeTheData/e6c856d1dad01e64e21fa0f7701759d4

Generation Details

openapi-generator-cli generate -g rust-axum -o outoutrs -i base.yaml

Steps to reproduce

Use the rust-axum generator with the provided declaration or any declaration using oneOf as a model content.

Related issues/PRs

Possibly #15, but most likely none.

Suggest a fix

I would replace the current implementation with one that uses enum serde representations instead.

Untagged would be used when discriminator is not defined:

#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum Message {
    Hello(Hello),
    Goodbye(Goodbye),
}

#[derive(Serialize, Deserialize)]
pub struct Hello {
    pub op: String,
    pub welcome_message: String,
}

#[derive(Serialize, Deserialize)]
pub struct Goodbye {
    pub op: String,
    pub goodbye_message: String,
}

Internally tagged enums are used when discriminator.propertyName is defined:

#[derive(Serialize, Deserialize)]
#[serde(tag = "op")]
pub enum Message {
    // ` mapping`s would lead to aliases
    #[serde(alias = "Hello", alias = "Greetings")]
    Hello(Hello),
    #[serde(alias = "Goodbye")]
    Goodbye(Goodbye),
}

// Note that tagged enums do not deserialize the tag to a field. If it were to be necessary, a possible
// solution to this would be to generate a method with the tag field name that returns the value.
// eg. `fn op() -> &'static str { "Hello" }`
#[derive(Serialize, Deserialize)]
pub struct Hello {
    pub welcome_message: String,
}

#[derive(Serialize, Deserialize)]
pub struct Goodbye {
    pub goodbye_message: String,
}

This solution could also be reused to support inline enums in the future, and oneOf enum mappings from OpenAPI 3.1 (see this stack overflow)

wing328 commented 1 week ago

if i'm not mistaken, the rust client generator supports oneOf.

can you please give it a try to see if you like the implementation?

Victoria-Casasampere-BeeTheData commented 1 week ago

The rust generator does create useful models, very similar to my suggested solution. It however does not do content validation and type matching (integers are always i32), so getting it ported will not be simple, but should be doable.