graphql-rust / graphql-client

Typed, correct GraphQL requests and responses in Rust
Apache License 2.0
1.12k stars 152 forks source link

Generate an enum for `@oneOf` input #450

Closed jbourassa closed 1 year ago

jbourassa commented 1 year ago

The RFC for oneOf on input type is currently in at the draft stage. In short, adding the @oneOf directive to an input type means exactly one field of the input's field must be provided.

While the spec still hasn't been accepted, there's a strong need for input unions, the spec is pretty far along and already seeing adoption in the community. It's available in the GraphQL Ruby gem, and we make use of it in Shopify Functions.

With this PR, inputs tagged with @oneOf will generate a Rust enum instead of a struct.

From the test, this type:

input Param @oneOf {
  author: Author
  name: String
  recursiveDirect: Param
  recursiveIndirect: Recursive
  requiredInts: [Int!]
  optionalInts: [Int]
}

generates this Rust enum:

pub enum Param {
    #[serde(rename = "author")]
    Author(Author),
    #[serde(rename = "name")]
    Name(String),
    #[serde(rename = "recursiveDirect")]
    RecursiveDirect(Box<Param>),
    #[serde(rename = "recursiveIndirect")]
    RecursiveIndirect(Box<Recursive>),
    #[serde(rename = "requiredInts")]
    RequiredInts(Vec<Int>),
    #[serde(rename = "optionalInts")]
    OptionalInts(Vec<Option<Int>>),
}

That is, it generates an enum with 1 variant per field, where the variant has 1 required field.


Worth noting that we'll only generate an enum when using IDL-defined schema (.graphql file), but not when using the introspection JSON response. The reason for that is that introspecting oneOf is done through the __Type.isOneOf field, which isn't widely implemented. To make it work, we'd have to dynamically select __Type.isOneOf in the introspection query, based on user opt-in. Since my use case didn't require it, and the introspection query is currently static, I didn't do it.

jbourassa commented 1 year ago

@surma this is another stab at addressing the same root problem as Shopify/shopify-function-rust#27, curious to hear your thoughts.