sui_gql_client/
extract.rs

1//! Defines [`extract!`](crate::extract!) and its [`Error`].
2
3#[cfg(feature = "queries")]
4pub(crate) type Result<T> = std::result::Result<T, Error>;
5
6/// Error for [`extract!`](crate::extract!).
7#[derive(thiserror::Error, Debug)]
8#[error("Missing data from response: {0}")]
9pub struct Error(pub(crate) String);
10
11impl Error {
12    pub fn new(s: impl Into<String>) -> Self {
13        Self(s.into())
14    }
15}
16
17/// Helper for extracting data from GraphQL responses.
18///
19/// Designed specially to deal with the multitude of nested [`Option`]s commonly found in GQL
20/// responses. The macro will generate an error string with the full path to the missing attribute.
21///
22/// # Example
23///
24/// ```no_run
25/// # use af_sui_types::Address;
26/// # use sui_gql_schema::{schema, scalars};
27/// #
28/// # #[derive(cynic::QueryVariables, Debug)]
29/// # struct QueryVariables<'a> {
30/// #     ch: Address,
31/// #     vault: DynamicFieldName<'a>,
32/// # }
33/// #
34/// # #[derive(cynic::InputObject, Debug)]
35/// # struct DynamicFieldName<'a> {
36/// #     #[cynic(rename = "type")]
37/// #     type_: &'a str,
38/// #     bcs: scalars::Base64<Vec<u8>>,
39/// # }
40/// #
41/// #[derive(cynic::QueryFragment, Debug)]
42/// #[cynic(variables = "QueryVariables")]
43/// struct Query {
44///     #[arguments(address: $ch)]
45///     object: Option<Object>,
46/// }
47///
48/// #[derive(cynic::QueryFragment, Debug)]
49/// #[cynic(variables = "QueryVariables")]
50/// struct Object {
51///     #[arguments(name: $vault)]
52///     dynamic_field: Option<DynamicField>,
53/// }
54///
55/// #[derive(cynic::QueryFragment, Debug)]
56/// struct DynamicField {
57///     value: Option<DynamicFieldValue>,
58/// }
59///
60/// #[derive(cynic::InlineFragments, Debug)]
61/// enum DynamicFieldValue {
62///     MoveValue(MoveValue),
63///     #[cynic(fallback)]
64///     Unknown
65/// }
66///
67/// #[derive(cynic::QueryFragment, Debug)]
68/// struct MoveValue {
69///     #[cynic(rename = "type")]
70///     type_: MoveType,
71///     bcs: scalars::Base64<Vec<u8>>,
72///     __typename: String,
73/// }
74///
75/// #[derive(cynic::QueryFragment, Debug)]
76/// struct MoveType {
77///     repr: String,
78/// }
79///
80/// use sui_gql_client::extract;
81///
82/// // Could be obtained in practice from `sui_gql_client::GraphQlResponseExt::try_into_data`
83/// let data: Option<Query> = None;
84/// let df_value: MoveValue = extract!(
85///     data?.object?.dynamic_field?.value?.as_variant(DynamicFieldValue::MoveValue)
86/// );
87/// # color_eyre::eyre::Ok(())
88/// ```
89#[macro_export]
90macro_rules! extract {
91    ($ident:ident $($tt:tt)*) => {{
92        $crate::extract!( @attributes [$ident][$($tt)*] -> {
93            $ident
94        } )
95    }};
96
97    (@attributes [$($path:tt)*][] -> { $($expr:tt)* } ) => { $($expr)* };
98
99    (@attributes [$($path:tt)*][.as_variant($($var:tt)+) $($tt:tt)*] -> { $($expr:tt)* } ) => {
100        $crate::extract!(@attributes
101            [$($path)*.as_variant($($var)*)][$($tt)*] -> {{
102                let value = $($expr)*;
103                let $($var)+(var) = value else {
104                    let msg = stringify!($($path)*.as_variant($($var)*));
105                    return Err($crate::extract::Error::new(msg).into());
106                };
107                var
108            }}
109        )
110    };
111
112    (@attributes [$($path:tt)*][? $($tt:tt)*] -> { $($expr:tt)* } ) => {
113        $crate::extract!(@attributes
114            [$($path)*][$($tt)*] -> {
115                $($expr)*
116                .ok_or_else(|| {
117                    let msg = stringify!($($path)*);
118                    $crate::extract::Error::new(msg)
119                })?
120            }
121        )
122    };
123
124    (@attributes [$($path:tt)*][.$ident:ident $($tt:tt)*] -> { $($expr:tt)* } ) => {
125        $crate::extract!(@attributes
126            [$($path)*.$ident][$($tt)*] -> {
127                $($expr)*
128                .$ident
129            }
130        )
131    };
132}