1use af_sui_types::{Address, ObjectArg, Version};
3use bimap::BiMap;
4use futures::TryStreamExt as _;
5use itertools::Itertools as _;
6use sui_gql_schema::scalars;
7
8use super::fragments::ObjectFilterV2;
9use super::objects_flat::Variables;
10use crate::{GraphQlClient, GraphQlErrors, GraphQlResponseExt as _, schema};
11
12type Query = super::objects_flat::Query<Object>;
13
14type Res<T, C> = std::result::Result<T, Error<<C as GraphQlClient>::Error>>;
17
18#[derive(thiserror::Error, Debug)]
19pub enum Error<T> {
20 #[error(transparent)]
21 Client(T),
22 #[error(transparent)]
23 Server(#[from] GraphQlErrors),
24 #[error("No data in object args query response")]
25 NoData,
26 #[error("Response missing object args for pairs: {0:?}")]
27 MissingNamedArgs(Vec<(String, Address)>),
28}
29
30pub(super) async fn query<C: GraphQlClient>(
34 client: &C,
35 mut names: BiMap<String, Address>,
36 page_size: Option<u32>,
37) -> Res<BiMap<String, ObjectArg>, C> {
38 let object_ids = names.right_values().cloned().collect_vec();
39 let filter = ObjectFilterV2 {
40 object_ids: Some(&object_ids),
41 type_: None,
42 owner: None,
43 };
44 let vars = Variables {
45 filter: Some(filter),
46 after: None,
47 first: page_size.map(|n| n as i32),
48 };
49
50 let mut stream = std::pin::pin!(super::stream::forward(client, vars, request));
51
52 let mut result = BiMap::new();
53
54 while let Some(arg) = stream.try_next().await? {
55 if let Some((key, _)) = names.remove_by_right(arg.id_borrowed()) {
56 result.insert(key, arg);
57 }
58 }
59
60 if !names.is_empty() {
61 return Err(Error::MissingNamedArgs(names.into_iter().collect()));
62 }
63
64 Ok(result)
65}
66
67async fn request<C: GraphQlClient>(
68 client: &C,
69 vars: Variables<'_>,
70) -> Res<super::stream::Page<impl Iterator<Item = Res<ObjectArg, C>> + 'static + use<C>>, C> {
71 let objects = client
72 .query::<Query, _>(vars)
73 .await
74 .map_err(Error::Client)?
75 .try_into_data()?
76 .ok_or(Error::NoData)?
77 .objects;
78
79 Ok(super::stream::Page {
80 info: objects.page_info.into(),
81 data: objects
82 .nodes
83 .into_iter()
84 .filter_map(Object::object_arg)
85 .map(Ok),
86 })
87}
88
89#[cfg(test)]
90#[allow(clippy::unwrap_used)]
91#[test]
92fn gql_output() {
93 use cynic::QueryBuilder as _;
94 let vars = Variables {
95 filter: None,
96 first: None,
97 after: None,
98 };
99 let operation = Query::build(vars);
100 insta::assert_snapshot!(operation.query, @r###"
101 query Query($filter: ObjectFilter, $after: String, $first: Int) {
102 objects(filter: $filter, first: $first, after: $after) {
103 nodes {
104 address
105 version
106 digest
107 owner {
108 __typename
109 ... on Immutable {
110 _
111 }
112 ... on Shared {
113 __typename
114 initialSharedVersion
115 }
116 ... on Parent {
117 __typename
118 }
119 ... on AddressOwner {
120 __typename
121 }
122 }
123 }
124 pageInfo {
125 hasNextPage
126 endCursor
127 }
128 }
129 }
130 "###);
131}
132
133#[macro_export]
164macro_rules! object_args {
165 (
167 { $name:ident: $object_id:expr_2021 $(,)? }
168 with { $client:expr_2021 }
169 ) => {
170 let $name = $crate::queries::GraphQlClientExt::object_arg($client, $object_id)
171 .await?;
172 };
173
174 (
175 { mut $name:ident: $object_id:expr_2021 $(,)? }
176 with { $client:expr_2021 }
177 ) => {
178 let $name = {
179 let mut oarg = $crate::queries::GraphQlClientExt::object_arg($client, $object_id)
180 .await?;
181 oarg.set_mutable(true)?;
182 oarg
183 };
184 };
185
186 (
187 {$($tt:tt)*}
188 with { $client:expr_2021 } $(paged by $page_size:expr_2021)?
189 ) => {
190 $crate::object_args!(@Names $($tt)*);
191 {
192 use $crate::queries::GraphQlClientExt as _;
193 let mut names = $crate::queries::BiMap::new();
194 $crate::object_args! { @Map names $($tt)* }
195 let mut oargs = $crate::queries::GraphQlClientExt::object_args(
196 $client,
197 names,
198 $crate::object_args!(@PageSize $($page_size)?)
199 ).await?;
200 $crate::object_args! { @Result oargs $($tt)* }
201 }
202 };
203
204 (@Names mut $name:ident: $_:expr_2021 $(, $($rest:tt)*)?) => {
205 $crate::object_args!(@Names $name: $_ $(, $($rest)*)?)
206 };
207
208 (@Names $name:ident: $_:expr_2021 $(, $($rest:tt)*)?) => {
209 let $name;
210 $crate::object_args!{ @Names $($($rest)*)? }
211 };
212
213 (@Names ) => {};
214
215 (@Map $map:ident mut $name:ident: $object_id:expr_2021 $(, $($rest:tt)*)?) => {
216 $crate::object_args! { @Map $map $name: $object_id $(, $($rest)*)? }
217 };
218
219 (@Map $map:ident $name:ident: $object_id:expr_2021 $(, $($rest:tt)*)?) => {
220 $map.insert(stringify!($name).to_owned(), $object_id);
221 $crate::object_args!{ @Map $map $($($rest)*)? }
222 };
223
224 (@Map $map:ident) => {};
225
226 (@Result $oargs:ident mut $name:ident: $_:expr_2021 $(, $($rest:tt)*)?) => {
227 let mut arg = $oargs
228 .remove_by_left(stringify!($name))
229 .expect("request_named_object_args should fail if any names are missing")
230 .1;
231 arg.set_mutable(true)?;
232 $name = arg;
233 $crate::object_args! {@Result $oargs $($($rest)*)?}
234 };
235
236 (@Result $oargs:ident $name:ident: $_:expr_2021 $(, $($rest:tt)*)?) => {
237 $name = $oargs
238 .remove_by_left(stringify!($name))
239 .expect("request_named_object_args should fail if any names are missing")
240 .1;
241 $crate::object_args! { @Result $oargs $($($rest)*)? }
242 };
243
244 (@Result $oargs:ident ) => {
245 };
246
247 (@PageSize $page_size:expr_2021) => { Some($page_size) };
248 (@PageSize) => { None };
249}
250
251#[derive(cynic::QueryFragment, Debug)]
256struct Object {
257 #[cynic(rename = "address")]
258 object_id: Address,
259 version: Version,
260 digest: Option<scalars::Digest>,
261 owner: Option<ObjectOwner>,
262}
263
264impl Object {
265 fn object_arg(self) -> Option<ObjectArg> {
269 let Self {
270 object_id,
271 version,
272 digest,
273 owner: Some(owner),
274 } = self
275 else {
276 return None;
277 };
278
279 build_object_arg_default(object_id, version, owner, digest)
280 }
281}
282
283pub(crate) fn build_object_arg_default(
284 id: Address,
285 version: Version,
286 owner: ObjectOwner,
287 digest: Option<scalars::Digest>,
288) -> Option<ObjectArg> {
289 Some(match owner {
290 ObjectOwner::Immutable(_) | ObjectOwner::Parent(_) | ObjectOwner::AddressOwner(_) => {
291 ObjectArg::ImmOrOwnedObject((id, version, digest?.0))
292 }
293 ObjectOwner::Shared(Shared {
294 initial_shared_version,
295 ..
296 }) => ObjectArg::SharedObject {
297 id,
298 initial_shared_version,
299 mutable: false,
300 },
301 ObjectOwner::Unknown => return None,
302 })
303}
304
305pub(super) fn build_oarg_set_mut(
306 object_id: Address,
307 version: Version,
308 owner: Option<ObjectOwner>,
309 digest: Option<scalars::Digest>,
310 mutable_: bool,
311) -> Option<ObjectArg> {
312 let mut oarg = build_object_arg_default(object_id, version, owner?, digest)?;
313 if let ObjectArg::SharedObject {
314 ref mut mutable, ..
315 } = oarg
316 {
317 *mutable = mutable_;
318 }
319 Some(oarg)
320}
321
322#[derive(cynic::InlineFragments, Debug)]
323pub(super) enum ObjectOwner {
324 #[allow(dead_code)]
325 Immutable(Immutable),
326
327 Shared(Shared),
328
329 #[allow(dead_code)]
330 Parent(Parent),
331
332 #[allow(dead_code)]
333 AddressOwner(AddressOwner),
334
335 #[cynic(fallback)]
336 Unknown,
337}
338
339#[derive(cynic::QueryFragment, Debug)]
340pub(super) struct Immutable {
341 #[cynic(rename = "_")]
342 __underscore: Option<bool>,
343}
344
345#[derive(cynic::QueryFragment, Debug)]
346pub(super) struct Shared {
347 __typename: String,
348 initial_shared_version: Version,
349}
350
351#[derive(cynic::QueryFragment, Debug)]
352pub(super) struct Parent {
353 __typename: String,
354}
355
356#[derive(cynic::QueryFragment, Debug)]
357pub(super) struct AddressOwner {
358 __typename: String,
359}