iota_sdk_graphql_client/api/
move_view_call.rs1use cynic::QueryBuilder;
5use iota_transaction_builder::types::MoveTypes;
6use iota_types::{Address, ObjectId, ObjectReference, TypeTag};
7
8use crate::{
9 GraphQLClient,
10 api::define_query,
11 error::GraphQLResult,
12 query_types::{MoveViewCallArgs, MoveViewCallQueryFragment, MoveViewResult},
13};
14
15define_query! {
16 pub struct MoveViewCallJsonQuery {
19 client: GraphQLClient,
20 function_name: String,
21 type_arguments: Option<Vec<String>>,
22 arguments: Option<Vec<serde_json::Value>>,
23 }
24 output: GraphQLResult<MoveViewResult>;
25}
26
27impl MoveViewCallJsonQuery {
28 pub fn type_arguments(mut self, type_arguments: Vec<String>) -> Self {
30 self.type_arguments = Some(type_arguments);
31 self
32 }
33
34 pub fn arguments(mut self, arguments: Vec<serde_json::Value>) -> Self {
36 self.arguments = Some(arguments);
37 self
38 }
39
40 async fn send(self) -> GraphQLResult<MoveViewResult> {
41 let operation = MoveViewCallQueryFragment::build(MoveViewCallArgs {
42 function_name: self.function_name,
43 type_arguments: self.type_arguments,
44 arguments: self.arguments,
45 });
46 let response = self.client.run_query(&operation).await?;
47
48 Ok(response.move_view_call)
49 }
50}
51
52define_query! {
53 pub struct MoveViewCallQuery {
56 client: GraphQLClient,
57 function_name: String,
58 type_arguments: Option<Vec<TypeTag>>,
59 arguments: Option<Vec<serde_json::Value>>,
60 }
61 output: GraphQLResult<MoveViewResult>;
62}
63
64impl MoveViewCallQuery {
65 pub fn type_arguments(mut self, type_arguments: Vec<TypeTag>) -> Self {
67 self.type_arguments = Some(type_arguments);
68 self
69 }
70
71 pub fn generics<G: MoveTypes>(mut self) -> Self {
74 self.type_arguments = Some(G::type_tags());
75 self
76 }
77
78 pub fn arguments(mut self, arguments: impl MoveViewArgList) -> Self {
81 self.arguments = Some(arguments.to_json_vec());
82 self
83 }
84
85 pub fn argument(mut self, argument: impl MoveViewArg) -> Self {
91 self.arguments
92 .get_or_insert_default()
93 .push(argument.to_json());
94 self
95 }
96
97 async fn send(self) -> GraphQLResult<MoveViewResult> {
98 let type_arguments = self
99 .type_arguments
100 .map(|tags| tags.into_iter().map(|t| t.to_string()).collect());
101 MoveViewCallJsonQuery {
102 client: self.client,
103 function_name: self.function_name,
104 type_arguments,
105 arguments: self.arguments,
106 }
107 .await
108 }
109}
110
111fn function_name(package: ObjectId, module: &str, function: &str) -> String {
114 format!("{package}::{module}::{function}")
115}
116
117impl GraphQLClient {
118 pub fn move_view_call_json(
136 &self,
137 package: impl Into<ObjectId>,
138 module: impl Into<String>,
139 function: impl Into<String>,
140 ) -> MoveViewCallJsonQuery {
141 MoveViewCallJsonQuery {
142 client: self.clone(),
143 function_name: function_name(package.into(), &module.into(), &function.into()),
144 type_arguments: None,
145 arguments: None,
146 }
147 }
148
149 pub fn move_view_call(
197 &self,
198 package: impl Into<ObjectId>,
199 module: impl Into<String>,
200 function: impl Into<String>,
201 ) -> MoveViewCallQuery {
202 MoveViewCallQuery {
203 client: self.clone(),
204 function_name: function_name(package.into(), &module.into(), &function.into()),
205 type_arguments: None,
206 arguments: None,
207 }
208 }
209}
210
211#[diagnostic::on_unimplemented(message = "Provided value is not a valid Move view argument.")]
213pub trait MoveViewArg {
214 fn to_json(self) -> serde_json::Value;
216}
217
218macro_rules! impl_move_view_arg_number {
220 ($($ty:ty),* $(,)?) => {
221 $(
222 impl MoveViewArg for $ty {
223 fn to_json(self) -> serde_json::Value {
224 serde_json::Value::Number(self.into())
225 }
226 }
227
228 impl MoveViewArg for &$ty {
229 fn to_json(self) -> serde_json::Value {
230 (*self).to_json()
231 }
232 }
233 )*
234 };
235}
236
237macro_rules! impl_move_view_arg_string {
239 ($($ty:ty),* $(,)?) => {
240 $(
241 impl MoveViewArg for $ty {
242 fn to_json(self) -> serde_json::Value {
243 serde_json::Value::String(self.to_string())
244 }
245 }
246
247 impl MoveViewArg for &$ty {
248 fn to_json(self) -> serde_json::Value {
249 (*self).to_json()
250 }
251 }
252 )*
253 };
254}
255
256impl MoveViewArg for bool {
257 fn to_json(self) -> serde_json::Value {
258 serde_json::Value::Bool(self)
259 }
260}
261
262impl MoveViewArg for &bool {
263 fn to_json(self) -> serde_json::Value {
264 (*self).to_json()
265 }
266}
267
268impl_move_view_arg_number!(u8, u16, u32);
269
270impl_move_view_arg_string!(u64, u128, ObjectId, Address);
272
273impl MoveViewArg for &str {
274 fn to_json(self) -> serde_json::Value {
275 serde_json::Value::String((*self).to_owned())
276 }
277}
278
279impl MoveViewArg for String {
280 fn to_json(self) -> serde_json::Value {
281 serde_json::Value::String(self)
282 }
283}
284
285impl MoveViewArg for &String {
286 fn to_json(self) -> serde_json::Value {
287 self.as_str().to_json()
288 }
289}
290
291impl MoveViewArg for ObjectReference {
292 fn to_json(self) -> serde_json::Value {
293 serde_json::Value::String(self.object_id.to_string())
294 }
295}
296
297impl MoveViewArg for &ObjectReference {
298 fn to_json(self) -> serde_json::Value {
299 serde_json::Value::String(self.object_id.to_string())
300 }
301}
302
303impl<T: MoveViewArg> MoveViewArg for Vec<T> {
305 fn to_json(self) -> serde_json::Value {
306 serde_json::Value::Array(self.into_iter().map(|v| v.to_json()).collect())
307 }
308}
309
310impl<T> MoveViewArg for &[T]
311where
312 for<'a> &'a T: MoveViewArg,
313{
314 fn to_json(self) -> serde_json::Value {
315 serde_json::Value::Array(self.iter().map(|v| v.to_json()).collect())
316 }
317}
318
319impl<const N: usize, T: MoveViewArg> MoveViewArg for [T; N] {
320 fn to_json(self) -> serde_json::Value {
321 serde_json::Value::Array(self.into_iter().map(|v| v.to_json()).collect())
322 }
323}
324
325impl<T: MoveViewArg> MoveViewArg for Option<T> {
326 fn to_json(self) -> serde_json::Value {
327 match self {
328 Some(v) => v.to_json(),
329 None => serde_json::Value::Null,
330 }
331 }
332}
333
334impl<T> MoveViewArg for std::sync::Arc<T>
336where
337 for<'a> &'a T: MoveViewArg,
338{
339 fn to_json(self) -> serde_json::Value {
340 self.as_ref().to_json()
341 }
342}
343
344impl<T> MoveViewArg for Box<T>
345where
346 for<'a> &'a T: MoveViewArg,
347{
348 fn to_json(self) -> serde_json::Value {
349 self.as_ref().to_json()
350 }
351}
352
353impl MoveViewArg for serde_json::Value {
355 fn to_json(self) -> serde_json::Value {
356 self
357 }
358}
359
360#[diagnostic::on_unimplemented(
362 message = "Provided value is not a valid list of Move view arguments.",
363 note = "Expected a tuple, vector, array, or slice of types that implement `MoveViewArg`."
364)]
365pub trait MoveViewArgList: sealed::Sealed {
366 fn to_json_vec(self) -> Vec<serde_json::Value>;
368}
369
370impl<T: MoveViewArg> MoveViewArgList for (T,) {
372 fn to_json_vec(self) -> Vec<serde_json::Value> {
373 vec![self.0.to_json()]
374 }
375}
376
377impl<T: MoveViewArg> MoveViewArgList for Vec<T> {
378 fn to_json_vec(self) -> Vec<serde_json::Value> {
379 self.into_iter().map(|v| v.to_json()).collect()
380 }
381}
382
383impl<const N: usize, T: MoveViewArg> MoveViewArgList for [T; N] {
384 fn to_json_vec(self) -> Vec<serde_json::Value> {
385 self.into_iter().map(|v| v.to_json()).collect()
386 }
387}
388
389impl<T> MoveViewArgList for &[T]
390where
391 for<'a> &'a T: MoveViewArg,
392{
393 fn to_json_vec(self) -> Vec<serde_json::Value> {
394 self.iter().map(|v| v.to_json()).collect()
395 }
396}
397
398macro_rules! impl_move_view_args_tuple {
400 ($(($n:tt, $T:ident)),*) => {
401 impl<$($T),+> sealed::Sealed for ($($T),+) {}
402
403 impl<$($T),+> MoveViewArgList for ($($T),+)
404 where $($T: MoveViewArg),+
405 {
406 fn to_json_vec(self) -> Vec<serde_json::Value> {
407 vec![
408 $(
409 self.$n.to_json()
410 ),+
411 ]
412 }
413 }
414 };
415}
416
417variadics_please::all_tuples_enumerated!(impl_move_view_args_tuple, 2, 15, T);
418
419mod sealed {
420 pub trait Sealed {}
421
422 impl<T> Sealed for (T,) {}
423 impl<T> Sealed for Vec<T> {}
424 impl<const N: usize, T> Sealed for [T; N] {}
425 impl<T> Sealed for &[T] {}
426}
427
428#[cfg(all(test, not(target_arch = "wasm32")))]
429mod tests {
430 use iota_types::{Address, ObjectId, TypeTag};
431
432 use crate::test_utils::sent_variables;
433
434 #[tokio::test]
435 async fn move_view_calls_send_the_function_type_arguments_and_arguments() {
436 let vars = sent_variables("MoveViewCallQueryFragment", |client| async move {
437 let _ = client
438 .move_view_call(Address::FRAMEWORK, "coin", "value")
439 .arguments((21u64,))
440 .type_arguments(vec![TypeTag::U64])
441 .await;
442 })
443 .await;
444 assert_eq!(
445 vars["functionName"],
446 format!("{}::coin::value", ObjectId::from(Address::FRAMEWORK))
447 );
448 assert_eq!(vars["typeArguments"], serde_json::json!(["u64"]));
449 assert_eq!(vars["arguments"], serde_json::json!(["21"]));
450
451 let vars = sent_variables("MoveViewCallQueryFragment", |client| async move {
452 let _ = client
453 .move_view_call_json(Address::FRAMEWORK, "coin", "value")
454 .type_arguments(vec!["u64".to_owned()])
455 .arguments(vec![serde_json::json!("21")])
456 .await;
457 })
458 .await;
459 assert_eq!(
460 vars["functionName"],
461 format!("{}::coin::value", ObjectId::from(Address::FRAMEWORK))
462 );
463 assert_eq!(vars["typeArguments"], serde_json::json!(["u64"]));
464 assert_eq!(vars["arguments"], serde_json::json!(["21"]));
465
466 let vars = sent_variables("MoveViewCallQueryFragment", |client| async move {
467 let _ = client
468 .move_view_call(Address::FRAMEWORK, "coin", "value")
469 .await;
470 })
471 .await;
472 assert!(vars["typeArguments"].is_null());
473 assert!(vars["arguments"].is_null());
474
475 let vars = sent_variables("MoveViewCallQueryFragment", |client| async move {
476 let _ = client
477 .move_view_call_json(Address::FRAMEWORK, "coin", "value")
478 .await;
479 })
480 .await;
481 assert!(vars["typeArguments"].is_null());
482 assert!(vars["arguments"].is_null());
483 }
484
485 #[tokio::test]
486 async fn move_view_calls_append_arguments_and_take_type_arguments_from_generics() {
487 let vars = sent_variables("MoveViewCallQueryFragment", |client| async move {
488 let _ = client
489 .move_view_call(Address::FRAMEWORK, "coin", "value")
490 .arguments((1u8, "a"))
491 .argument(u64::MAX)
492 .argument(vec![2u8, 3])
493 .generics::<(u64, String)>()
494 .await;
495 })
496 .await;
497 assert_eq!(
498 vars["arguments"],
499 serde_json::json!([1, "a", u64::MAX.to_string(), [2, 3]])
500 );
501 assert_eq!(
502 vars["typeArguments"],
503 serde_json::json!(["u64", "vector<u8>"])
504 );
505
506 let vars = sent_variables("MoveViewCallQueryFragment", |client| async move {
507 let _ = client
508 .move_view_call(Address::FRAMEWORK, "coin", "value")
509 .argument(21u64)
510 .await;
511 })
512 .await;
513 assert_eq!(vars["arguments"], serde_json::json!(["21"]));
514 }
515}