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
532 downloads per month
Used in 5 crates
(3 directly)
20KB
239 lines
Macro to extract data from deeply nested types representing GraphQL results
Suggested workflow
- Generate query types using cynic and its generator
- Use insta to define an inline snapshot test so that the query string is visible in the module that defines the query types
- Define an
extract
function that takes the root query type and returns the data of interest - Inside
extract
, useextract!
asextract!(data => { ... })
- Inside the curly braces, past the query string from the snapshot test above
- Change all node names from
camelCase
tosnake_case
- Add
?
after the nodes that are nullable - Add
[]
after the nodes that are iterable
Examples
The following omits the derive
s 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