sui_gql_client/queries/
owner_df_contents.rs

1use std::collections::HashMap;
2
3use af_sui_types::Address as SuiAddress;
4
5use super::Error;
6use super::fragments::MoveValueRaw;
7use super::outputs::{DynamicField as OutputDf, ObjectKey, RawMoveValue};
8use crate::{GraphQlClient, GraphQlResponseExt as _, missing_data, schema};
9
10pub async fn query<C: GraphQlClient>(
11    client: &C,
12    address: SuiAddress,
13    root_version: Option<u64>,
14    first: Option<i32>,
15    after: Option<String>,
16) -> Result<(HashMap<RawMoveValue, OutputDf>, Option<String>), Error<C::Error>> {
17    let vars = QueryVariables {
18        address,
19        root_version,
20        first,
21        after,
22    };
23    let data = client
24        .query::<Query, QueryVariables>(vars)
25        .await
26        .map_err(Error::Client)?
27        .try_into_data()?
28        .ok_or(missing_data!("Response empty"))?;
29
30    let DynamicFieldConnection { nodes, page_info } = data
31        .owner
32        .ok_or(missing_data!("No owner found"))?
33        .dynamic_fields;
34
35    let next_cursor = if page_info.has_next_page {
36        page_info.end_cursor
37    } else {
38        None
39    };
40
41    let mut df_map = HashMap::new();
42    for DynamicField { name, value } in nodes {
43        let name = name
44            .ok_or(missing_data!("Dynamic field found but with no name"))?
45            .into();
46        let instance = value.ok_or(missing_data!("Dynamic field found but with no value"))?;
47        let out = match instance {
48            DynamicFieldValue::MoveObject(MoveObject {
49                object_id,
50                version,
51                contents,
52            }) => {
53                let struct_ = contents
54                    .ok_or(missing_data!("No contents for DOF"))?
55                    .try_into()
56                    .expect("Only Move structs can be top-level objects");
57                OutputDf::Object(ObjectKey { object_id, version }, struct_)
58            }
59            DynamicFieldValue::MoveValue(value) => OutputDf::Field(value.into()),
60            DynamicFieldValue::Unknown => return Err(missing_data!("Unknown dynamic field type")),
61        };
62        df_map.insert(name, out);
63    }
64
65    Ok((df_map, next_cursor))
66}
67
68#[cfg(test)]
69#[allow(clippy::unwrap_used)]
70#[test]
71fn gql_output() {
72    use cynic::QueryBuilder as _;
73    let vars = QueryVariables {
74        address: SuiAddress::new(rand::random()),
75        root_version: None,
76        first: None,
77        after: None,
78    };
79    let operation = Query::build(vars);
80    insta::assert_snapshot!(operation.query, @r###"
81    query Query($address: SuiAddress!, $rootVersion: UInt53, $after: String, $first: Int) {
82      owner(address: $address, rootVersion: $rootVersion) {
83        dynamicFields(first: $first, after: $after) {
84          nodes {
85            name {
86              type {
87                repr
88              }
89              bcs
90            }
91            value {
92              __typename
93              ... on MoveObject {
94                address
95                version
96                contents {
97                  type {
98                    repr
99                  }
100                  bcs
101                }
102              }
103              ... on MoveValue {
104                type {
105                  repr
106                }
107                bcs
108              }
109            }
110          }
111          pageInfo {
112            hasNextPage
113            endCursor
114          }
115        }
116      }
117    }
118    "###);
119}
120
121// ================================================================================
122//  Mostly autogenerated by: https://generator.cynic-rs.dev/
123// ================================================================================
124
125#[derive(cynic::QueryVariables, Debug)]
126struct QueryVariables {
127    address: SuiAddress,
128    root_version: Option<af_sui_types::Version>,
129    after: Option<String>,
130    first: Option<i32>,
131}
132
133#[derive(cynic::QueryFragment, Debug)]
134#[cynic(variables = "QueryVariables")]
135struct Query {
136    #[arguments(address: $address, rootVersion: $root_version)]
137    owner: Option<Owner>,
138}
139
140#[derive(cynic::QueryFragment, Debug)]
141#[cynic(variables = "QueryVariables")]
142struct Owner {
143    #[arguments(first: $first, after: $after)]
144    dynamic_fields: DynamicFieldConnection,
145}
146
147#[derive(cynic::QueryFragment, Debug)]
148struct MoveObject {
149    #[cynic(rename = "address")]
150    object_id: SuiAddress,
151    version: af_sui_types::Version,
152    contents: Option<MoveValueRaw>,
153}
154
155#[derive(cynic::QueryFragment, Debug)]
156struct DynamicFieldConnection {
157    nodes: Vec<DynamicField>,
158    page_info: PageInfo,
159}
160
161#[derive(cynic::QueryFragment, Debug)]
162struct PageInfo {
163    has_next_page: bool,
164    end_cursor: Option<String>,
165}
166
167#[derive(cynic::QueryFragment, Debug)]
168struct DynamicField {
169    name: Option<MoveValueRaw>,
170    value: Option<DynamicFieldValue>,
171}
172
173#[derive(cynic::InlineFragments, Debug)]
174enum DynamicFieldValue {
175    MoveObject(MoveObject),
176    MoveValue(MoveValueRaw),
177    #[cynic(fallback)]
178    Unknown,
179}