af_iperps/graphql/
ch_positions.rs

1use af_move_type::MoveInstance;
2use af_sui_types::{Address, Version};
3use enum_as_inner::EnumAsInner;
4use futures::Stream;
5pub use sui_gql_client::queries::Error;
6use sui_gql_client::queries::GraphQlClientExt as _;
7use sui_gql_client::queries::fragments::{MoveValueRaw, PageInfoForward};
8use sui_gql_client::{GraphQlClient, GraphQlResponseExt as _, schema};
9
10type Position = MoveInstance<crate::position::Position>;
11
12pub(super) fn query<C: GraphQlClient>(
13    client: &C,
14    ch: Address,
15    version: Option<Version>,
16) -> impl Stream<Item = Result<(u64, Position), Error<C::Error>>> + '_ {
17    async_stream::try_stream! {
18        let mut vars = Variables {
19            ch,
20            version,
21            first: Some(client.max_page_size().await?),
22            after: None,
23        };
24        let mut has_next_page = true;
25        while has_next_page {
26            let (page_info, positions) = request(client, vars.clone()).await?;
27
28            vars.after = page_info.end_cursor.clone();
29            has_next_page = page_info.has_next_page;
30
31            for value in positions {
32                yield value;
33            }
34        }
35    }
36}
37
38async fn request<C: GraphQlClient>(
39    client: &C,
40    vars: Variables,
41) -> Result<
42    (
43        PageInfoForward,
44        impl Iterator<Item = (u64, Position)> + 'static,
45    ),
46    Error<C::Error>,
47> {
48    let response = client
49        .query::<Query, _>(vars)
50        .await
51        .map_err(Error::Client)?;
52    let data = response.try_into_data()?;
53
54    let ChDfsConnection { nodes, page_info } = extract(data)?;
55    Ok((page_info, nodes.into_iter().filter_map(filter_df)))
56}
57
58fn extract(data: Option<Query>) -> Result<ChDfsConnection, &'static str> {
59    graphql_extract::extract!(data => {
60        clearing_house? {
61            dfs
62        }
63    });
64    Ok(dfs)
65}
66
67fn filter_df(df: ChDf) -> Option<(u64, Position)> {
68    let df_name: MoveInstance<crate::keys::Position> = df.df_name?.try_into().ok()?;
69    let df_value_raw = df.df_value?.into_move_value().ok();
70    let df_value: Position = df_value_raw?.try_into().ok()?;
71
72    Some((df_name.value.account_id, df_value))
73}
74
75#[cfg(test)]
76#[allow(clippy::unwrap_used)]
77#[test]
78fn gql_output() {
79    use cynic::QueryBuilder as _;
80
81    let vars = Variables {
82        ch: Address::ZERO,
83        version: None,
84        first: Some(10),
85        after: None,
86    };
87    let operation = Query::build(vars);
88    insta::assert_snapshot!(operation.query, @r###"
89    query Query($ch: SuiAddress!, $version: UInt53, $first: Int, $after: String) {
90      clearing_house: object(address: $ch, version: $version) {
91        dfs: dynamicFields(first: $first, after: $after) {
92          nodes {
93            df_name: name {
94              type {
95                repr
96              }
97              bcs
98            }
99            df_value: value {
100              __typename
101              ... on MoveValue {
102                type {
103                  repr
104                }
105                bcs
106              }
107            }
108          }
109          pageInfo {
110            hasNextPage
111            endCursor
112          }
113        }
114      }
115    }
116    "###);
117}
118
119#[derive(cynic::QueryVariables, Clone, Debug)]
120struct Variables {
121    ch: Address,
122    version: Option<Version>,
123    first: Option<i32>,
124    after: Option<String>,
125}
126
127#[derive(cynic::QueryFragment, Debug)]
128#[cynic(variables = "Variables")]
129struct Query {
130    #[arguments(address: $ch, version: $version)]
131    #[cynic(alias, rename = "object")]
132    clearing_house: Option<ClearingHouseObject>,
133}
134
135#[derive(cynic::QueryFragment, Debug)]
136#[cynic(graphql_type = "Object", variables = "Variables")]
137struct ClearingHouseObject {
138    #[arguments(first: $first, after: $after)]
139    #[cynic(alias, rename = "dynamicFields")]
140    dfs: ChDfsConnection,
141}
142
143#[derive(cynic::QueryFragment, Debug)]
144#[cynic(graphql_type = "DynamicFieldConnection")]
145struct ChDfsConnection {
146    nodes: Vec<ChDf>,
147    page_info: PageInfoForward,
148}
149
150#[derive(cynic::QueryFragment, Debug)]
151#[cynic(graphql_type = "DynamicField")]
152struct ChDf {
153    #[cynic(alias, rename = "name")]
154    df_name: Option<MoveValueRaw>,
155    #[cynic(alias, rename = "value")]
156    df_value: Option<ChDfValue>,
157}
158
159#[derive(cynic::InlineFragments, Debug, EnumAsInner)]
160#[cynic(graphql_type = "DynamicFieldValue")]
161enum ChDfValue {
162    MoveValue(MoveValueRaw),
163    #[cynic(fallback)]
164    Unknown,
165}