sui_gql_client/queries/
owner_df_contents_stream.rs

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