openapi-generator icon indicating copy to clipboard operation
openapi-generator copied to clipboard

[BUG][RUST] oneOf with multiple arrays produces invalid rust code

Open felixauringer opened this issue 10 months ago • 5 comments

Bug Report Checklist

  • [x] Have you provided a full/minimal spec to reproduce the issue?
  • [x] Have you validated the input using an OpenAPI validator (example)?
  • [x] Have you tested with the latest master to confirm the issue still exists?
  • [x] Have you searched for related issues/PRs?
  • [x] What's the actual output vs expected output?
  • [ ] [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

If there is no title, the rust generator tries to derive a meaningful name from the type (see https://github.com/OpenAPITools/openapi-generator/issues/17896). However, this fails in some cases, for example if there is a oneOf with multiple arrays inside.

The OpenAPI spec below produces the following enum:

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Option1OrOption2Options {
    Array(Vec<String>),
    Array(Vec<i32>),
}

This cannot be compiled, the error message is error[E0428]: the name 'Array' is defined multiple times. I would expect that all code generated from a valid OpenAPI spec generates compilable rust code.

openapi-generator version

I tested with version 06ed7c82056aace4651d65ec56b68e4520739b53 (the current main).

OpenAPI declaration file content or url
openapi: 3.0.0
info:
  title: oneOf with arrays
  description: Ensure different names for kinds in enum when using oneOf with arrays.
  version: 1.0.0
paths: {}
components:
  schemas:
    Option1OrOption2:
      type: object
      properties:
        Options:
          oneOf:
            - type: array
              items:
                type: string
            - type: array
              items:
                type: integer
Steps to reproduce

I'll provide the steps to create a failing test using the valid OpenAPI spec above.

  • Add the snippet above to the test resources (e.g. modules/openapi-generator/src/test/resources/3_0/rust/oneof-with-arrays.yaml).

  • Create a config for the test (e.g. bin/configs/rust-reqwest-oneOf-with-arrays.yaml):

    generatorName: rust
    outputDir: samples/client/others/rust/reqwest/oneOf-with-arrays
    library: reqwest
    inputSpec: modules/openapi-generator/src/test/resources/3_0/rust/oneof-with-arrays.yaml
    templateDir: modules/openapi-generator/src/main/resources/rust
    additionalProperties:
      supportAsync: false
      packageName: oneof-with-arrays-reqwest
    
  • Generate the rust samples with ./bin/generate-samples.sh ./bin/configs/rust-*.

  • Execute the tests with mvn integration-test -f samples/client/others/rust/pom.xml.

Related issues/PRs
  • Not long ago, the rust type was directly used as the enum kind, see https://github.com/OpenAPITools/openapi-generator/issues/17896.
  • This was changed in https://github.com/OpenAPITools/openapi-generator/pull/17915 solving https://github.com/OpenAPITools/openapi-generator/issues/17896.
  • https://github.com/OpenAPITools/openapi-generator/issues/17210 is a follow up issue but it's different from the issue I am opening here.
Suggest a fix

I'm new to rust and to openapi-generator, so I am no help here, sorry.

felixauringer avatar Apr 28 '24 18:04 felixauringer

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Option1OrOption2Options {
    Array(Vec<String>),
    Array(Vec<i32>),
}

ideally what's the correct/expected code look like?

wing328 avatar Apr 30 '24 07:04 wing328

There is probably not a really nice way as there is just not much information. Maybe one could enumerate the enum fields:

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Option1OrOption2Options {
    Array1(Vec<String>),
    Array2(Vec<i32>),
}

Those are not helpful variable names but at least it could be compiled. Maybe the generators for the other languages in this project have a nicer way to solve this?

felixauringer avatar May 05 '24 19:05 felixauringer

what about the following instead?

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Option1OrOption2Options {
    ArrayVecString(Vec<String>),
    ArrayVeci32(Vec<i32>),
}

wing328 avatar May 06 '24 06:05 wing328

Theoretically, the following is also a valid OpenAPI spec:

openapi: 3.0.0
info:
  title: oneOf with arrays
  description: Ensure different names for kinds in enum when using oneOf with arrays.
  version: 1.0.0
paths: {}
components:
  schemas:
    Option1OrOption2:
      type: object
      properties:
        Options:
          oneOf:
            - type: array
              items:
                type: string
            - type: array
              items:
                type: string

With your proposition, this would result in a name clash again. However, depending on whether you want to support all edge cases of OpenAPI, that might be okay because the semantics of the spec above are not clear (why even have a oneOf if both alternatives are exactly the same?).

felixauringer avatar May 06 '24 06:05 felixauringer

I found something similar to this today. We have something like this:

openapi: 3.0.0
info:
  title: oneOf with arrays
  description: Ensure different names for kinds in enum when using oneOf with arrays.
  version: 1.0.0
paths: {}
components:
  schemas:
    Option1OrOption2:
      type: object
      properties:
        Options:
          oneOf:
            - type: string
              filter: '^\d{4}$'
            - type: string
              maxLength: 0

This should also generate distinct types. This also has another problem as this will currently create a String enum member which is already defined for the native Rust's String type. I would also think that just generating distinct names would be a good idea. Then you can map this name with --type-mappings.

nagua avatar Jun 21 '24 13:06 nagua

Any further plans on tackling this?

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub enum Option1OrOption2Options { ArrayVecString(Vec<String>), ArrayVeci32(Vec), }

Your suggestion is the imo the most idiomatic solution. If someone could point me in the direction of where the name variable is constructed in the java source, I'll get it done.

geoffreygarrett avatar Jul 28 '24 08:07 geoffreygarrett

@wing328

geoffreygarrett avatar Jul 28 '24 08:07 geoffreygarrett