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