Skip to content

Deserialize GraphQL enum into user defined Rust enum #358

@jakmeier

Description

@jakmeier

Hi,

I have been using this crate for more than a year in the client of a hobby project browser game. Love this crate, thanks a bunch for the great work!

One thing has been bothering me for a while. In the code generated for a query, is it possible to define an existing Rust enum to use for deserialization instead of generating a new one? I'm thinking of something akin to custom scalars, where any Rust object with the same name as the GraphQL type defined in the schema can be used, as long as it implements Deserialize.

As far as I can tell, nothing like that is supported today, is that correct? I'm happy to dig into the code and see if I could come up with a PR as an implementation suggestion, if the maintainers of this crate think it would be a worthwhile feature to add.

Motivation

My motivation for this is the following.

I have several Rust enums defined in a crate that I import in the client and the server. The Rust enums are simple C-style enumerations without fields, hence I map them to GraphQL enums.

The server uses Juniper to generate and serve a GraphQL interface. Using the graphql_client_cli, I create a schema.json from a running backend instance. Then I generate the code for the queries. This generates new enum types for each query.

So far so good. But I want to use the same enum type in the client as used by the backend. Thus, I have to manually translate from the generate enum back to the original enum type. I have to do this for every query where the enum is used. When I add a enum variant, I have to change the translation code for all queries.

Being able to specify to deserialize into my Rust enum directly would save me a lot of boiler-plate code.

Example

Shared Rust enum used by client and server:

#[derive(Clone, Copy, Serialize, Deserialize)]
#[cfg_attr(feature = "backend", derive(juniper::GraphQLEnum))]
pub enum BuildingType {
    Tree,
    Temple,
}

Schema generated by Juniper:

{
          "description": null,
          "enumValues": [
            {
              "deprecationReason": null,
              "description": null,
              "isDeprecated": false,
              "name": "TREE"
            },
            {
              "deprecationReason": null,
              "description": null,
              "isDeprecated": false,
              "name": "TEMPLE"
            }
          ],
          "fields": null,
          "inputFields": null,
          "interfaces": null,
          "kind": "ENUM",
          "name": "BuildingType",
          "possibleTypes": null
        },

Query:

query BuildingsQuery($village_id: Int!) {
  village(villageId: $village_id) {
    buildings {
      id
      buildingType
    }
  }
}

Manual translation I currently need to do:

impl Into<BuildingType> for &buildings_query::BuildingType {
    fn into(self) -> BuildingType {
        match self {
            buildings_query::BuildingType::TREE => BuildingType::Tree,
            buildings_query::BuildingType::TEMPLE => BuildingType::Temple,
            buildings_query::BuildingType::Other(_) => panic!("Unexpected BuildingType"),
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions