Skip to main content

af_iperps/graphql/
map_orders.rs

1use af_move_type::MoveInstance;
2use af_sui_types::Address;
3use enum_as_inner::EnumAsInner;
4use futures::Stream;
5pub use sui_gql_client::queries::Error;
6use sui_gql_client::queries::model::fragments::{MoveValueGql, PageInfoForward};
7use sui_gql_client::{GraphQlClient, GraphQlResponseExt as _, schema};
8
9use crate::orderbook::Order;
10use crate::ordered_map::Leaf;
11
12pub(super) fn query<C: GraphQlClient>(
13    client: &C,
14    map: Address,
15    at_checkpoint: Option<u64>,
16) -> impl Stream<Item = Result<(u128, Order), Error<C::Error>>> + '_ {
17    async_stream::try_stream! {
18        let mut vars = Variables {
19            map,
20            at_checkpoint,
21            first: Some(32),
22            after: None,
23        };
24        let mut has_next_page = true;
25        while has_next_page {
26            let (page_info, orders) = 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 orders {
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 = (u128, Order)> + '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 MapDfsConnection { nodes, page_info } = extract(data)?;
55    Ok((page_info, nodes.into_iter().flat_map(MapDf::into_orders)))
56}
57
58fn extract(data: Option<Query>) -> Result<MapDfsConnection, &'static str> {
59    graphql_extract::extract!(data => {
60        map? {
61            map_dfs?
62        }
63    });
64    Ok(map_dfs)
65}
66
67#[cfg(test)]
68#[allow(clippy::unwrap_used)]
69#[test]
70fn gql_output() {
71    use cynic::QueryBuilder as _;
72
73    let vars = Variables {
74        map: Address::ZERO,
75        at_checkpoint: None,
76        first: Some(10),
77        after: None,
78    };
79    let operation = Query::build(vars);
80    insta::assert_snapshot!(operation.query, @r"
81    query Query($map: SuiAddress!, $atCheckpoint: UInt53, $first: Int, $after: String) {
82      map: object(address: $map, atCheckpoint: $atCheckpoint) {
83        map_dfs: dynamicFields(first: $first, after: $after) {
84          nodes {
85            map_df: value {
86              __typename
87              ... on MoveValue {
88                type {
89                  repr
90                }
91                bcs
92              }
93            }
94          }
95          pageInfo {
96            hasNextPage
97            endCursor
98          }
99        }
100      }
101    }
102    ");
103}
104
105#[derive(cynic::QueryVariables, Clone, Debug)]
106struct Variables {
107    map: Address,
108    at_checkpoint: Option<u64>,
109    first: Option<i32>,
110    after: Option<String>,
111}
112
113#[derive(cynic::QueryFragment, Debug)]
114#[cynic(variables = "Variables")]
115struct Query {
116    #[arguments(address: $map, atCheckpoint: $at_checkpoint)]
117    #[cynic(alias, rename = "object")]
118    map: Option<MapAsOwner>,
119}
120
121#[derive(cynic::QueryFragment, Debug)]
122#[cynic(graphql_type = "Object", variables = "Variables")]
123struct MapAsOwner {
124    #[arguments(first: $first, after: $after)]
125    #[cynic(alias, rename = "dynamicFields")]
126    map_dfs: Option<MapDfsConnection>,
127}
128
129#[derive(cynic::QueryFragment, Debug)]
130#[cynic(graphql_type = "DynamicFieldConnection")]
131struct MapDfsConnection {
132    nodes: Vec<MapDf>,
133    page_info: PageInfoForward,
134}
135
136#[derive(cynic::QueryFragment, Debug)]
137#[cynic(graphql_type = "DynamicField")]
138struct MapDf {
139    #[cynic(alias, rename = "value")]
140    map_df: Option<MapDfValue>,
141}
142
143impl MapDf {
144    fn into_orders(self) -> impl Iterator<Item = (u128, Order)> {
145        self.map_df
146            .into_iter()
147            .map(MapDfValue::into_move_value)
148            .filter_map(Result::ok)
149            .map(MoveInstance::<Leaf<Order>>::try_from)
150            // If deser. fails, we just assume it was a `Branch` instead of a `Leaf`
151            .filter_map(Result::ok)
152            .flat_map(|leaf| Vec::from(leaf.value.keys_vals).into_iter())
153            .map(|pair| (pair.key, pair.val))
154    }
155}
156
157#[derive(cynic::InlineFragments, Debug, EnumAsInner)]
158#[cynic(graphql_type = "DynamicFieldValue")]
159enum MapDfValue {
160    MoveValue(MoveValueGql),
161    #[cynic(fallback)]
162    Unknown,
163}