sui_gql_schema/
scalars.rs

1use std::str::FromStr;
2
3use af_sui_types::{Address as SuiAddress, encoding};
4use cynic::impl_scalar;
5use derive_more::with_trait::{AsRef, Deref, Display, From, Into};
6use serde::{Deserialize, Serialize};
7use serde_json::Value as Json;
8use serde_with::{Bytes, DisplayFromStr, base64, serde_as};
9
10use crate::schema;
11
12macro_rules! scalar_with_generics {
13    (
14        impl<$($T:ident),+> $schema:ident::$scalar:ident for $type_:ty $(where { $($bounds:tt)+ })?
15    ) => {
16        impl<$($T),+> cynic::schema::IsScalar<$schema::$scalar> for $type_
17        $(where $($bounds)+)?
18        {
19            type SchemaType = $schema::$scalar;
20        }
21
22        impl<$($T),+> cynic::coercions::CoercesTo<$schema::$scalar> for $type_
23        $(where $($bounds)+)?
24        {
25        }
26
27        impl<$($T),+> $schema::variable::Variable for $type_
28        $(where $($bounds)+)?
29        {
30            const TYPE: cynic::variables::VariableType = cynic::variables::VariableType::Named(
31                <$schema::$scalar as cynic::schema::NamedType>::NAME,
32            );
33        }
34    };
35}
36
37// =============================================================================
38//  Base64
39// =============================================================================
40
41/// Base64-encoded data. Received from the server as a string.
42///
43/// From the schema: "String containing Base64-encoded binary data."
44#[serde_as]
45#[derive(AsRef, Clone, Deref, Deserialize, Serialize)]
46#[as_ref(forward)]
47pub struct Base64<T>(#[serde_as(as = "base64::Base64")] T)
48where
49    T: AsRef<[u8]> + From<Vec<u8>>;
50
51impl<T> Base64<T>
52where
53    T: AsRef<[u8]> + From<Vec<u8>>,
54{
55    pub const fn new(value: T) -> Self {
56        Self(value)
57    }
58
59    pub fn into_inner(self) -> T {
60        self.0
61    }
62}
63
64impl<T> std::fmt::Debug for Base64<T>
65where
66    T: AsRef<[u8]> + From<Vec<u8>>,
67{
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        write!(
70            f,
71            "Base64({})",
72            af_sui_types::encode_base64_default(&self.0)
73        )
74    }
75}
76
77scalar_with_generics! {
78    impl<T> schema::Base64 for Base64<T> where {
79        T: AsRef<[u8]> + From<Vec<u8>>,
80    }
81}
82
83#[serde_as]
84#[derive(AsRef, Clone, Debug, Deref, Deserialize, Serialize)]
85#[as_ref(forward)]
86#[serde(bound(deserialize = "T: for<'a> Deserialize<'a>"))]
87#[serde(bound(serialize = "T: Serialize"))]
88pub struct Base64Bcs<T>(#[serde_as(as = "encoding::Base64Bcs")] T);
89
90impl<T> Base64Bcs<T> {
91    pub fn into_inner(self) -> T {
92        self.0
93    }
94}
95
96scalar_with_generics! {
97    impl<T> schema::Base64 for Base64Bcs<T>
98}
99
100// =============================================================================
101//  BigInt
102// =============================================================================
103
104/// Generic integer. Received from the server as a string.
105///
106/// From the schema: "String representation of an arbitrary width, possibly signed integer."
107#[serde_as]
108#[derive(Clone, Debug, Display, Deserialize, Serialize)]
109pub struct BigInt<T>(#[serde_as(as = "DisplayFromStr")] T)
110where
111    T: Display + FromStr,
112    T::Err: Display;
113
114impl<T> BigInt<T>
115where
116    T: Display + FromStr,
117    T::Err: Display,
118{
119    pub fn into_inner(self) -> T {
120        self.0
121    }
122}
123
124scalar_with_generics! {
125    impl<T> schema::BigInt for BigInt<T>
126    where {
127        T: Display + FromStr,
128        T::Err: Display,
129    }
130}
131
132// =============================================================================
133//  DateTime
134// =============================================================================
135
136impl_scalar!(DateTime, schema::DateTime);
137
138/// ISO-8601 Date and Time: RFC3339 in UTC with format: YYYY-MM-DDTHH:MM:SS.mmmZ. Note that the
139/// milliseconds part is optional, and it may be omitted if its value is 0.
140#[serde_as]
141#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq, Into, Display, Deref)]
142pub struct DateTime(#[serde_as(as = "DisplayFromStr")] chrono::DateTime<chrono::Utc>);
143
144// =============================================================================
145//  JSON
146// =============================================================================
147
148impl_scalar!(Json, schema::JSON);
149
150// =============================================================================
151//  MoveData
152// =============================================================================
153
154impl_scalar!(MoveData, schema::MoveData);
155
156/// The contents of a Move Value, corresponding to the following recursive type:
157///
158/// type MoveData =
159///     { Address: SuiAddress }
160///   | { UID:     SuiAddress }
161///   | { ID:      SuiAddress }
162///   | { Bool:    bool }
163///   | { Number:  BigInt }
164///   | { String:  string }
165///   | { Vector:  [MoveData] }
166///   | { Option:   MoveData? }
167///   | { Struct:  [{ name: string, value: MoveData }] }
168///   | { Variant: {
169///       name: string,
170///       fields: [{ name: string, value: MoveData }],
171///   }
172#[serde_as]
173#[derive(Deserialize, Serialize, Clone, Debug)]
174pub enum MoveData {
175    Address(#[serde_as(as = "Bytes")] [u8; 32]),
176    #[serde(rename = "UID")]
177    Uid(#[serde_as(as = "Bytes")] [u8; 32]),
178    #[serde(rename = "ID")]
179    Id(#[serde_as(as = "Bytes")] [u8; 32]),
180    Bool(bool),
181    Number(String),
182    String(String),
183    Vector(Vec<MoveData>),
184    Option(Option<Box<MoveData>>),
185    Struct(Vec<MoveField>),
186    Variant(MoveVariant),
187}
188
189#[derive(Deserialize, Serialize, Clone, Debug)]
190pub struct MoveVariant {
191    name: String,
192    fields: Vec<MoveField>,
193}
194
195#[derive(Deserialize, Serialize, Clone, Debug)]
196pub struct MoveField {
197    pub name: String,
198    pub value: MoveData,
199}
200
201// =============================================================================
202//  MoveTypeLayout
203// =============================================================================
204
205impl_scalar!(MoveTypeLayout, schema::MoveTypeLayout);
206
207#[doc = r#"The shape of a concrete Move Type (a type with all its type parameters instantiated with
208concrete types), corresponding to the following recursive type:
209
210type MoveTypeLayout =
211    "address"
212  | "bool"
213  | "u8" | "u16" | ... | "u256"
214  | { vector: MoveTypeLayout }
215  | {
216      struct: {
217        type: string,
218        fields: [{ name: string, layout: MoveTypeLayout }],
219      }
220    }
221  | { enum: [{
222          type: string,
223          variants: [{
224              name: string,
225              fields: [{ name: string, layout: MoveTypeLayout }],
226          }]
227      }]
228    }"#]
229#[derive(Clone, Debug, Deserialize, Serialize)]
230#[serde(rename_all = "camelCase")]
231pub enum MoveTypeLayout {
232    Address,
233    Bool,
234    U8,
235    U16,
236    U32,
237    U64,
238    U128,
239    U256,
240    Vector(Box<MoveTypeLayout>),
241    Struct(MoveStructLayout),
242    Enum(MoveEnumLayout),
243}
244
245#[derive(Clone, Debug, Deserialize, Serialize)]
246pub struct MoveEnumLayout {
247    pub variants: Vec<MoveVariantLayout>,
248}
249
250#[derive(Clone, Debug, Deserialize, Serialize)]
251pub struct MoveVariantLayout {
252    pub name: String,
253    pub layout: Vec<MoveFieldLayout>,
254}
255
256#[derive(Clone, Debug, Deserialize, Serialize)]
257pub struct MoveStructLayout {
258    #[serde(rename = "type")]
259    type_: String,
260    fields: Vec<MoveFieldLayout>,
261}
262
263#[derive(Clone, Debug, Deserialize, Serialize)]
264pub struct MoveFieldLayout {
265    name: String,
266    layout: MoveTypeLayout,
267}
268
269// =============================================================================
270//  MoveTypeSignature
271// =============================================================================
272
273impl_scalar!(MoveTypeSignature, schema::MoveTypeSignature);
274
275#[doc = r#"The signature of a concrete Move Type (a type with all its type parameters instantiated
276with concrete types, that contains no references), corresponding to the following recursive type:
277
278type MoveTypeSignature =
279    "address"
280  | "bool"
281  | "u8" | "u16" | ... | "u256"
282  | { vector: MoveTypeSignature }
283  | {
284      datatype: {
285        package: string,
286        module: string,
287        type: string,
288        typeParameters: [MoveTypeSignature],
289      }
290    }"#]
291#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
292#[serde(rename_all = "camelCase")]
293pub enum MoveTypeSignature {
294    Address,
295    Bool,
296    U8,
297    U16,
298    U32,
299    U64,
300    U128,
301    U256,
302    Vector(Box<MoveTypeSignature>),
303    Datatype {
304        package: String,
305        module: String,
306        #[serde(rename = "type")]
307        type_: String,
308        #[serde(rename = "typeParameters")]
309        type_parameters: Vec<MoveTypeSignature>,
310    },
311}
312
313// =============================================================================
314//  OpenMoveTypeSignature
315// =============================================================================
316
317impl_scalar!(OpenMoveTypeSignature, schema::OpenMoveTypeSignature);
318
319#[doc = r#"The shape of an abstract Move Type (a type that can contain free type parameters, and can
320optionally be taken by reference), corresponding to the following recursive type:
321
322type OpenMoveTypeSignature = {
323  ref: ("&" | "&mut")?,
324  body: OpenMoveTypeSignatureBody,
325}
326
327type OpenMoveTypeSignatureBody =
328    "address"
329  | "bool"
330  | "u8" | "u16" | ... | "u256"
331  | { vector: OpenMoveTypeSignatureBody }
332  | {
333      datatype {
334        package: string,
335        module: string,
336        type: string,
337        typeParameters: [OpenMoveTypeSignatureBody]
338      }
339    }
340  | { typeParameter: number }"#]
341#[derive(Serialize, Deserialize, Clone, Debug)]
342pub struct OpenMoveTypeSignature {
343    #[serde(rename = "ref")]
344    ref_: Option<OpenMoveTypeReference>,
345    body: OpenMoveTypeSignatureBody,
346}
347
348#[derive(Serialize, Deserialize, Clone, Debug)]
349pub enum OpenMoveTypeReference {
350    #[serde(rename = "&")]
351    Immutable,
352
353    #[serde(rename = "&mut")]
354    Mutable,
355}
356
357#[derive(Serialize, Deserialize, Clone, Debug)]
358#[serde(rename_all = "camelCase")]
359pub enum OpenMoveTypeSignatureBody {
360    TypeParameter(u16),
361    Address,
362    Bool,
363    U8,
364    U16,
365    U32,
366    U64,
367    U128,
368    U256,
369    Vector(Box<OpenMoveTypeSignatureBody>),
370    Datatype {
371        package: String,
372        module: String,
373        #[serde(rename = "type")]
374        type_: String,
375        #[serde(rename = "typeParameters")]
376        type_parameters: Vec<OpenMoveTypeSignatureBody>,
377    },
378}
379
380// =============================================================================
381//  SuiAddress:
382//  String containing 32B hex-encoded address, with a leading "0x". Leading
383//  zeroes can be omitted on input but will always appear in outputs (SuiAddress
384//  in output is guaranteed to be 66 characters long).
385// =============================================================================
386
387impl_scalar!(SuiAddress, schema::SuiAddress);
388
389// =============================================================================
390//  Extras
391// =============================================================================
392
393impl_scalar!(Digest, schema::String);
394impl_scalar!(TypeTag, schema::String);
395
396#[derive(Clone, Debug, Deserialize)]
397pub struct Digest(pub af_sui_types::Digest);
398
399/// Newtype for using [`af_sui_types::TypeTag`] in GQL queries.
400#[serde_as]
401#[derive(Clone, Debug, Deserialize, Serialize)]
402pub struct TypeTag(#[serde_as(as = "DisplayFromStr")] pub af_sui_types::TypeTag);
403
404// =============================================================================
405//  UInt53
406// =============================================================================
407
408impl_scalar!(af_sui_types::Version, schema::UInt53);
409
410// =============================================================================
411//  Tests
412// =============================================================================
413
414#[cfg(test)]
415mod tests {
416    use color_eyre::Result;
417
418    use super::*;
419
420    /// Taken from
421    ///
422    /// ```grapql
423    /// query Events($first: Int, $after: String, $filter: EventFilter) {
424    ///   events(after: $after, first: $first, filter: $filter) {
425    ///     edges {
426    ///       node {
427    ///         timestamp
428    ///         type {
429    ///           signature
430    ///         }
431    ///         json
432    ///       }
433    ///       cursor
434    ///     }
435    ///     pageInfo {
436    ///       hasNextPage
437    ///     }
438    ///   }
439    /// }
440    /// ```
441    /// Variables:
442    /// ```json
443    /// {
444    ///   "filter": {
445    ///     "eventType": "0xfd6f306bb2f8dce24dd3d4a9bdc51a46e7c932b15007d73ac0cfb38c15de0fea::events"
446    ///   }
447    /// }
448    /// ```
449    const MOVE_TYPE_SIGNATURE_JSON: &str = r#"{
450        "datatype": {
451          "package": "0xfd6f306bb2f8dce24dd3d4a9bdc51a46e7c932b15007d73ac0cfb38c15de0fea",
452          "module": "events",
453          "type": "DepositedCollateral",
454          "typeParameters": []
455        }
456    }"#;
457
458    #[test]
459    fn move_type_signature_serde() -> Result<()> {
460        let sig: MoveTypeSignature = serde_json::from_str(MOVE_TYPE_SIGNATURE_JSON)?;
461        assert_eq!(
462            sig,
463            MoveTypeSignature::Datatype {
464                package: "0xfd6f306bb2f8dce24dd3d4a9bdc51a46e7c932b15007d73ac0cfb38c15de0fea"
465                    .into(),
466                module: "events".into(),
467                type_: "DepositedCollateral".into(),
468                type_parameters: vec![],
469            }
470        );
471        Ok(())
472    }
473}