af_iperps/graphql/
map_orders.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
10use crate::orderbook::Order;
11use crate::ordered_map::Leaf;
12
13pub(super) fn query<C: GraphQlClient>(
14    client: &C,
15    map: Address,
16    ch_version: Option<Version>,
17) -> impl Stream<Item = Result<(u128, Order), Error<C::Error>>> + '_ {
18    async_stream::try_stream! {
19        let mut vars = Variables {
20            map,
21            ch_version,
22            first: Some(client.max_page_size().await?),
23            after: None,
24        };
25        let mut has_next_page = true;
26        while has_next_page {
27            let (page_info, orders) = request(client, vars.clone()).await?;
28
29            vars.after = page_info.end_cursor.clone();
30            has_next_page = page_info.has_next_page;
31
32            for value in orders {
33                yield value;
34            }
35        }
36    }
37}
38
39async fn request<C: GraphQlClient>(
40    client: &C,
41    vars: Variables,
42) -> Result<
43    (
44        PageInfoForward,
45        impl Iterator<Item = (u128, Order)> + 'static,
46    ),
47    Error<C::Error>,
48> {
49    let response = client
50        .query::<Query, _>(vars)
51        .await
52        .map_err(Error::Client)?;
53    let data = response.try_into_data()?;
54
55    let MapDfsConnection { nodes, page_info } = extract(data)?;
56    Ok((page_info, nodes.into_iter().flat_map(MapDf::into_orders)))
57}
58
59fn extract(data: Option<Query>) -> Result<MapDfsConnection, &'static str> {
60    graphql_extract::extract!(data => {
61        map? {
62            map_dfs
63        }
64    });
65    Ok(map_dfs)
66}
67
68#[cfg(test)]
69#[allow(clippy::unwrap_used)]
70#[test]
71fn gql_output() {
72    use cynic::QueryBuilder as _;
73
74    let vars = Variables {
75        map: Address::ZERO,
76        ch_version: None,
77        first: Some(10),
78        after: None,
79    };
80    let operation = Query::build(vars);
81    insta::assert_snapshot!(operation.query, @r###"
82    query Query($map: SuiAddress!, $chVersion: UInt53, $first: Int, $after: String) {
83      map: owner(address: $map, rootVersion: $chVersion) {
84        map_dfs: dynamicFields(first: $first, after: $after) {
85          nodes {
86            map_df: value {
87              __typename
88              ... on MoveValue {
89                type {
90                  repr
91                }
92                bcs
93              }
94            }
95          }
96          pageInfo {
97            hasNextPage
98            endCursor
99          }
100        }
101      }
102    }
103    "###);
104}
105
106#[derive(cynic::QueryVariables, Clone, Debug)]
107struct Variables {
108    map: Address,
109    ch_version: Option<Version>,
110    first: Option<i32>,
111    after: Option<String>,
112}
113
114#[derive(cynic::QueryFragment, Debug)]
115#[cynic(variables = "Variables")]
116struct Query {
117    #[arguments(address: $map, rootVersion: $ch_version)]
118    #[cynic(alias, rename = "owner")]
119    map: Option<MapAsOwner>,
120}
121
122#[derive(cynic::QueryFragment, Debug)]
123#[cynic(graphql_type = "Owner", variables = "Variables")]
124struct MapAsOwner {
125    #[arguments(first: $first, after: $after)]
126    #[cynic(alias, rename = "dynamicFields")]
127    map_dfs: MapDfsConnection,
128}
129
130#[derive(cynic::QueryFragment, Debug)]
131#[cynic(graphql_type = "DynamicFieldConnection")]
132struct MapDfsConnection {
133    nodes: Vec<MapDf>,
134    page_info: PageInfoForward,
135}
136
137#[derive(cynic::QueryFragment, Debug)]
138#[cynic(graphql_type = "DynamicField")]
139struct MapDf {
140    #[cynic(alias, rename = "value")]
141    map_df: Option<MapDfValue>,
142}
143
144impl MapDf {
145    fn into_orders(self) -> impl Iterator<Item = (u128, Order)> {
146        self.map_df
147            .into_iter()
148            .map(MapDfValue::into_move_value)
149            .filter_map(Result::ok)
150            .map(MoveInstance::<Leaf<Order>>::try_from)
151            // If deser. fails, we just assume it was a `Branch` instead of a `Leaf`
152            .filter_map(Result::ok)
153            .flat_map(|leaf| Vec::from(leaf.value.keys_vals).into_iter())
154            .map(|pair| (pair.key, pair.val))
155    }
156}
157
158#[derive(cynic::InlineFragments, Debug, EnumAsInner)]
159#[cynic(graphql_type = "DynamicFieldValue")]
160enum MapDfValue {
161    MoveValue(MoveValueRaw),
162    #[cynic(fallback)]
163    Unknown,
164}