1#![cfg_attr(all(doc, not(doctest)), feature(doc_cfg))]
2
3pub use cynic;
53use cynic::schema::{MutationRoot, QueryRoot};
54use cynic::serde::Serialize;
55use cynic::serde::de::DeserializeOwned;
56use cynic::{GraphQlError, GraphQlResponse, Operation, QueryFragment, QueryVariables};
57use extension_traits::extension;
58pub use sui_gql_schema::{scalars, schema};
59
60pub mod queries;
61mod raw_client;
62pub mod reqwest;
63
64#[deprecated(since = "0.14.8", note = "use the graphql-extract crate")]
65pub mod extract;
66mod paged;
67
68pub use self::paged::{Paged, PagedResponse, PagesDataResult};
69pub use self::raw_client::{Error as RawClientError, RawClient};
70
71#[trait_variant::make(Send)]
73pub trait GraphQlClient: Sync {
74 type Error: std::error::Error + Send + 'static;
75
76 async fn query_paged<Init>(&self, vars: Init::Input) -> Result<PagedResponse<Init>, Self::Error>
77 where
78 Init: Paged + Send + 'static,
79 Init::SchemaType: QueryRoot,
80 Init::Input: Clone,
81 Init::NextPage:
82 Paged<Input = Init::NextInput, NextInput = Init::NextInput, NextPage = Init::NextPage>,
83 <Init::NextPage as QueryFragment>::SchemaType: QueryRoot,
84 <Init::NextPage as Paged>::Input: Clone,
85 {
86 async {
87 let initial: GraphQlResponse<Init> = self.query(vars.clone()).await?;
88 let mut next_vars = initial.data.as_ref().and_then(|d| d.next_variables(vars));
89 let mut pages = vec![];
90 while let Some(vars) = next_vars {
91 let next_page: GraphQlResponse<Init::NextPage> = self.query(vars.clone()).await?;
92 next_vars = next_page.data.as_ref().and_then(|d| d.next_variables(vars));
93 pages.push(next_page);
94 }
95 Ok(PagedResponse(initial, pages))
96 }
97 }
98
99 async fn query<Query, Variables>(
100 &self,
101 vars: Variables,
102 ) -> Result<GraphQlResponse<Query>, Self::Error>
103 where
104 Variables: QueryVariables + Send + Serialize,
105 Query: DeserializeOwned + QueryFragment<VariablesFields = Variables::Fields> + 'static,
106 Query::SchemaType: QueryRoot,
107 {
108 use cynic::QueryBuilder as _;
109 self.run_graphql(Query::build(vars))
110 }
111
112 async fn mutation<Mutation, Vars>(
113 &self,
114 vars: Vars,
115 ) -> Result<GraphQlResponse<Mutation>, Self::Error>
116 where
117 Vars: QueryVariables + Send + Serialize,
118 Mutation: DeserializeOwned + QueryFragment<VariablesFields = Vars::Fields> + 'static,
119 Mutation::SchemaType: MutationRoot,
120 {
121 use cynic::MutationBuilder as _;
122 self.run_graphql(Mutation::build(vars))
123 }
124
125 async fn run_graphql<Query, Vars>(
126 &self,
127 operation: Operation<Query, Vars>,
128 ) -> Result<GraphQlResponse<Query>, Self::Error>
129 where
130 Vars: Serialize + Send,
131 Query: DeserializeOwned + 'static;
132}
133
134#[extension(pub trait GraphQlResponseExt)]
136impl<T> GraphQlResponse<T> {
137 fn try_into_data(self) -> Result<Option<T>, GraphQlErrors> {
140 if let Some(errors) = self.errors {
141 if !errors.is_empty() {
142 return Err(GraphQlErrors { errors, page: None });
143 }
144 }
145
146 let Some(data) = self.data else {
147 return Ok(None);
148 };
149 Ok(Some(data))
150 }
151}
152
153#[derive(thiserror::Error, Clone, Debug, Eq, PartialEq, serde::Deserialize)]
155pub struct GraphQlErrors<Extensions = serde::de::IgnoredAny> {
156 pub errors: Vec<GraphQlError<Extensions>>,
157 pub page: Option<usize>,
158}
159
160impl<Extensions> std::fmt::Display for GraphQlErrors<Extensions> {
161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 let page_info = self
163 .page
164 .map_or_else(String::new, |page| format!(" at page {page}"));
165 writeln!(
166 f,
167 "Query execution produced the following errors{page_info}:"
168 )?;
169 for error in &self.errors {
170 writeln!(f, "{error}")?;
171 }
172 Ok(())
173 }
174}