sui_gql_client/
extract.rs

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