#result #extract #iterable #workflow #generator #above #nullable #interest #graphql-extract

macro graphql-extract

Macro to extract data from deeply nested types representing GraphQL results

8 releases

Uses new Rust 2024

new 0.0.8 May 14, 2025
0.0.7 Mar 21, 2025
0.0.5 Feb 22, 2025
0.0.2 Jan 29, 2025

#7 in #nullable

Download history 207/week @ 2025-01-24 438/week @ 2025-01-31 183/week @ 2025-02-07 88/week @ 2025-02-14 316/week @ 2025-02-21 219/week @ 2025-02-28 271/week @ 2025-03-07 166/week @ 2025-03-14 353/week @ 2025-03-21 190/week @ 2025-03-28 237/week @ 2025-04-04 179/week @ 2025-04-11 128/week @ 2025-04-18 59/week @ 2025-04-25 62/week @ 2025-05-02 249/week @ 2025-05-09

532 downloads per month
Used in 5 crates (3 directly)

Apache-2.0

20KB
239 lines

Macro to extract data from deeply nested types representing GraphQL results

Suggested workflow

  1. Generate query types using cynic and its generator
  2. Use insta to define an inline snapshot test so that the query string is visible in the module that defines the query types
  3. Define an extract function that takes the root query type and returns the data of interest
  4. Inside extract, use extract! as extract!(data => { ... })
  5. Inside the curly braces, past the query string from the snapshot test above
  6. Change all node names from camelCase to snake_case
  7. Add ? after the nodes that are nullable
  8. Add [] after the nodes that are iterable

Examples

The following omits the derives for cynic traits that are usually implemented for GraphQL queries. This is so that we can focus on the nesting of the structures and how the macro helps to 'extract' the leaves.

struct Query {
    object: Option<Object>,
}

struct Object {
    dynamic_field: Option<DynamicField>,
}

struct DynamicField {
    value: Option<DynamicFieldValue>,
}

enum DynamicFieldValue {
    MoveValue(MoveValue),
    Unknown,
}

struct MoveValue {
    type_: MoveType,
    bcs: Option<String>,
}

struct MoveType {
    repr: String,
}

fn extract(data: Option<Query>) -> Result<(MoveType, String), &'static str> {
    use graphql_extract::extract;
    use DynamicFieldValue::MoveValue;

    // Leafs become available as variables
    extract!(data => {
        object? {
            dynamic_field? {
                value? {
                    // `MoveValue` is the enum variant name we're interested in
                    ... on MoveValue {
                        type_
                        bcs?
                    }
                }
            }
        }
    });
    Ok((type_, bcs))
}
struct Query {
    address: Option<Address2>,
    object: Option<Object>,
}

struct Address2 {
    address: String,
}

struct Object {
    version: u64,
    dynamic_field: Option<DynamicField>,
    dynamic_fields: DynamicFieldConnection,
}

struct DynamicFieldConnection {
    nodes: Vec<DynamicField>,
}

struct DynamicField {
    value: Option<DynamicFieldValue>,
}

enum DynamicFieldValue {
    MoveValue(MoveValue),
    Unknown,
}

struct MoveValue {
    type_: MoveType,
    bcs: String,
}

struct MoveType {
    repr: String,
}

type Item = Result<(MoveType, String), &'static str>;

fn extract(data: Option<Query>) -> Result<(u64, impl Iterator<Item = Item>), &'static str> {
    use graphql_extract::extract;
    use DynamicFieldValue::MoveValue;

    extract!(data => {
        object? {
            version
            dynamic_fields {
                // `nodes` becomes a variable in the namespace. It implements `Iterator`
                nodes[] {
                    // Everything underneath an iterator node works the same, except it 'maps'
                    // the items of the iterator (check the `Item` type alias above)
                    value? {
                        ... on MoveValue {
                            type_
                            bcs
                        }
                    }
                }
            }
        }
    });
    Ok((version, nodes))
}

A caveat to the above is that nested iterator[] nodes aren't handled yet. They'll likely be forbidden in the future.

Dependencies

~200–640KB
~15K SLoC