sui_gql_client/
paged.rs

1use cynic::serde::de::DeserializeOwned;
2use cynic::serde::Serialize;
3use cynic::{GraphQlResponse, QueryFragment, QueryVariables};
4use tap::TapFallible as _;
5
6use crate::{GraphQlErrors, GraphQlResponseExt as _};
7
8/// Pages resulting from GraphQL queries.
9///
10/// The full responses are included to allow users to introspect possible errors.
11pub struct PagedResponse<Init>(
12    pub(crate) GraphQlResponse<Init>,
13    pub(crate) Vec<GraphQlResponse<Init::NextPage>>,
14)
15where
16    Init: Paged;
17
18impl<Init> PagedResponse<Init>
19where
20    Init: Paged,
21{
22    /// Extract data (inital query, subsequent queries) from the [GraphQlResponse]s.
23    ///
24    /// Errors if any response has errors.
25    pub fn try_into_data(self) -> PagesDataResult<Init> {
26        let Self(first, next) = self;
27
28        let Some(initial) = first.try_into_data().tap_err_mut(|e| e.page = Some(0))? else {
29            return Ok(None);
30        };
31
32        let mut pages = vec![];
33        for (i, response) in next.into_iter().enumerate() {
34            if let Some(page_data) = response
35                .try_into_data()
36                .tap_err_mut(|e| e.page = Some(i + 1))?
37            {
38                pages.push(page_data);
39            } else {
40                break;
41            }
42        }
43
44        Ok(Some((initial, pages)))
45    }
46
47    pub fn into_inner(self) -> (GraphQlResponse<Init>, Vec<GraphQlResponse<Init::NextPage>>) {
48        (self.0, self.1)
49    }
50}
51
52/// The initial page data and subsequent ones', if any.
53pub type PagesDataResult<T> = Result<Option<(T, Vec<<T as Paged>::NextPage>)>, GraphQlErrors>;
54
55/// Interface for paged queries to allow automatic pagination.
56pub trait Paged:
57    DeserializeOwned + QueryFragment<VariablesFields = <Self::Input as QueryVariables>::Fields>
58{
59    /// The input for this query type.
60    type Input: QueryVariables + Send + Serialize;
61    /// The type of the next query variables.
62    type NextInput: QueryVariables + Send + Serialize;
63    /// The type of the next query.
64    type NextPage: DeserializeOwned
65        + QueryFragment<VariablesFields = <Self::NextInput as QueryVariables>::Fields>
66        + Send;
67
68    /// The next query variables to use for querying the next page, if any.
69    fn next_variables(&self, prev_vars: Self::Input) -> Option<Self::NextInput>;
70}