af_move_type/
lib.rs

1#![cfg_attr(all(doc, not(doctest)), feature(doc_auto_cfg))]
2
3//! Defines the core standard for representing Move types off-chain and their type tags.
4//!
5//! The core items are [`MoveType`](crate::MoveType) and [`MoveTypeTag`](crate::MoveTypeTag). These
6//! are useful trait bounds to use when dealing with generic off-chain Move type representations.
7//! They are implemented for the primitive types that correspond to Move's primitives
8//! (integers/bool). Also included is [`MoveVec`](crate::vector::MoveVec), corresponding to `vector`
9//! and defining a pretty [`Display`](::std::fmt::Display).
10//!
11//! For Move structs (objects), [`MoveStruct`](crate::MoveStruct) should be used as it has an
12//! associated [`MoveStructTag`](crate::MoveStructTag). The
13//! [`MoveStruct`](af_move_type_derive::MoveStruct) derive macro is exported for automatically
14//! creating a `MoveStructTag` implementation from normal Rust struct declarations.
15//!
16//! A specific instance of a Move type is represented by [`MoveInstance`](crate::MoveInstance).
17use std::fmt::Debug;
18use std::hash::Hash;
19use std::str::FromStr;
20
21pub use af_move_type_derive::MoveStruct;
22use af_sui_types::u256::U256;
23use af_sui_types::{Address, IdentStr, Identifier, ObjectId, StructTag, TypeTag};
24use serde::{Deserialize, Serialize};
25
26#[doc(hidden)]
27pub mod external;
28pub mod otw;
29pub mod vector;
30
31// =============================================================================
32//  Errors
33// =============================================================================
34
35#[derive(thiserror::Error, Debug)]
36pub enum TypeTagError {
37    #[error("Wrong TypeTag variant: expected {expected}, got {got}")]
38    Variant { expected: String, got: TypeTag },
39    #[error("StructTag params: {0}")]
40    StructTag(#[from] StructTagError),
41}
42
43#[derive(thiserror::Error, Debug)]
44pub enum StructTagError {
45    #[error("Wrong address: expected {expected}, got {got}")]
46    Address { expected: Address, got: Address },
47    #[error("Wrong module: expected {expected}, got {got}")]
48    Module {
49        expected: Identifier,
50        got: Identifier,
51    },
52    #[error("Wrong name: expected {expected}, got {got}")]
53    Name {
54        expected: Identifier,
55        got: Identifier,
56    },
57    #[error("Wrong type parameters: {0}")]
58    TypeParams(#[from] TypeParamsError),
59}
60
61#[derive(thiserror::Error, Debug)]
62pub enum TypeParamsError {
63    #[error("Wrong number of generics: expected {expected}, got {got}")]
64    Number { expected: usize, got: usize },
65    #[error("Wrong type for generic: {0}")]
66    TypeTag(Box<TypeTagError>),
67}
68
69impl From<TypeTagError> for TypeParamsError {
70    fn from(value: TypeTagError) -> Self {
71        Self::TypeTag(Box::new(value))
72    }
73}
74
75#[derive(thiserror::Error, Debug)]
76pub enum ParseTypeTagError {
77    #[error("Parsing TypeTag: {0}")]
78    FromStr(#[from] sui_sdk_types::TypeParseError),
79    #[error("Converting from TypeTag: {0}")]
80    TypeTag(#[from] TypeTagError),
81}
82
83#[derive(thiserror::Error, Debug)]
84pub enum ParseStructTagError {
85    #[error("Parsing StructTag: {0}")]
86    FromStr(#[from] sui_sdk_types::TypeParseError),
87    #[error("Converting from StructTag: {0}")]
88    StructTag(#[from] StructTagError),
89}
90
91#[derive(thiserror::Error, Debug)]
92pub enum FromRawTypeError {
93    #[error("Converting from TypeTag: {0}")]
94    TypeTag(#[from] TypeTagError),
95    #[error("Deserializing BCS: {0}")]
96    Bcs(#[from] bcs::Error),
97}
98
99#[derive(thiserror::Error, Debug)]
100pub enum FromRawStructError {
101    #[error("Converting from StructTag: {0}")]
102    StructTag(#[from] StructTagError),
103    #[error("Deserializing BCS: {0}")]
104    Bcs(#[from] bcs::Error),
105}
106
107// =============================================================================
108//  MoveType
109// =============================================================================
110
111/// Trait marking a Move data type. Has a specific way to construct a `TypeTag`.
112pub trait MoveType:
113    Clone
114    + std::fmt::Debug
115    + std::fmt::Display
116    + for<'de> Deserialize<'de>
117    + Serialize
118    + PartialEq
119    + Eq
120    + std::hash::Hash
121{
122    type TypeTag: MoveTypeTag;
123
124    /// Deserialize the contents of the Move type from BCS bytes.
125    fn from_bcs(bytes: &[u8]) -> bcs::Result<Self> {
126        bcs::from_bytes(bytes)
127    }
128
129    /// Consuming version of [`to_bcs`](MoveType::to_bcs).
130    fn into_bcs(self) -> bcs::Result<Vec<u8>> {
131        bcs::to_bytes(&self)
132    }
133
134    /// Serialize the contents of the Move type to BCS bytes.
135    fn to_bcs(&self) -> bcs::Result<Vec<u8>> {
136        bcs::to_bytes(self)
137    }
138
139    /// Consuming version of [`to_json`](MoveType::to_json).
140    fn into_json(self) -> serde_json::Value {
141        let mut value = serde_json::json!(self);
142        // Move only uses integer values, for which the JSON encoding uses strings
143        number_to_string_value_recursive(&mut value);
144        value
145    }
146
147    /// Serialize the contents of the Move type to JSON.
148    ///
149    /// The method takes care to use JSON [`String`](serde_json::Value::String) representations for
150    /// integer types, for which [`serde`] would use [`Number`](serde_json::Value::Number).
151    ///
152    /// This is useful for interacting with the RPC.
153    fn to_json(&self) -> serde_json::Value {
154        let mut value = serde_json::json!(self);
155        // Move only uses integer values, for which the JSON encoding uses strings
156        number_to_string_value_recursive(&mut value);
157        value
158    }
159}
160
161pub trait MoveTypeTag:
162    Into<TypeTag>
163    + TryFrom<TypeTag, Error = TypeTagError>
164    + FromStr
165    + Clone
166    + Debug
167    + PartialEq
168    + Eq
169    + Hash
170    + for<'de> Deserialize<'de>
171    + PartialOrd
172    + Ord
173    + Serialize
174{
175}
176
177impl<T> MoveTypeTag for T where
178    T: Into<TypeTag>
179        + TryFrom<TypeTag, Error = TypeTagError>
180        + FromStr
181        + Clone
182        + Debug
183        + PartialEq
184        + Eq
185        + Hash
186        + for<'de> Deserialize<'de>
187        + PartialOrd
188        + Ord
189        + Serialize
190{
191}
192
193// =============================================================================
194//  MoveStruct
195// =============================================================================
196
197/// Trait marking a Move struct type. Has a specific way to construct a `StructTag`.
198pub trait MoveStruct: MoveType<TypeTag = Self::StructTag> {
199    type StructTag: MoveStructTag;
200}
201
202pub trait MoveStructTag:
203    Into<StructTag> + TryFrom<StructTag, Error = StructTagError> + MoveTypeTag
204{
205}
206
207impl<T> MoveStructTag for T where
208    T: Into<StructTag> + TryFrom<StructTag, Error = StructTagError> + MoveTypeTag
209{
210}
211
212// =============================================================================
213//  Abilities
214// =============================================================================
215
216pub trait HasKey: MoveStruct {
217    fn object_id(&self) -> ObjectId;
218}
219
220pub trait HasCopy: MoveStruct + Copy {}
221
222pub trait HasStore: MoveStruct {}
223
224pub trait HasDrop: MoveStruct {}
225
226// =============================================================================
227//  Static attributes
228// =============================================================================
229
230/// Move type for which the type tag can be derived at compile time.
231pub trait StaticTypeTag: MoveType {
232    fn type_() -> Self::TypeTag;
233
234    fn type_tag() -> TypeTag {
235        Self::type_().into()
236    }
237}
238
239/// Move struct for which the address of the package is known at compile time.
240pub trait StaticAddress: MoveStruct {
241    fn address() -> Address;
242}
243
244/// Move struct for which the module in the package is known at compile time.
245pub trait StaticModule: MoveStruct {
246    fn module() -> Identifier;
247}
248
249/// Move struct for which the name of object is known at compile time.
250pub trait StaticName: MoveStruct {
251    fn name() -> Identifier;
252}
253
254/// Move struct for which the type args of object are known at compile time.
255pub trait StaticTypeParams: MoveStruct {
256    fn type_params() -> Vec<TypeTag>;
257}
258
259/// Move struct for which the struct tag can be derived at compile time.
260pub trait StaticStructTag: MoveStruct {
261    fn struct_tag() -> StructTag;
262}
263
264impl<T> StaticStructTag for T
265where
266    T: StaticAddress + StaticModule + StaticName + StaticTypeParams,
267{
268    fn struct_tag() -> StructTag {
269        StructTag {
270            address: Self::address(),
271            module: Self::module(),
272            name: Self::name(),
273            type_params: Self::type_params(),
274        }
275    }
276}
277
278// =============================================================================
279//  MoveInstance
280// =============================================================================
281
282/// Represents an instance of a Move type.
283///
284/// Both `type_` and `value` are necessary to represent an instance since otherwise there would be
285/// ambiguity, e.g., when the same package is published twice on-chain.
286#[derive(Clone, Debug, PartialEq, Eq, Hash)]
287pub struct MoveInstance<T: MoveType> {
288    pub type_: T::TypeTag,
289    pub value: T,
290}
291
292impl<T: StaticTypeTag> From<T> for MoveInstance<T> {
293    fn from(value: T) -> Self {
294        Self {
295            type_: T::type_(),
296            value,
297        }
298    }
299}
300
301impl<T: MoveStruct + tabled::Tabled> std::fmt::Display for MoveInstance<T> {
302    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
303        use tabled::settings::panel::Header;
304        use tabled::settings::{Rotate, Settings, Style};
305        use tabled::Table;
306
307        let stag: StructTag = self.type_.clone().into();
308        let settings = Settings::default()
309            .with(Rotate::Left)
310            .with(Rotate::Top)
311            .with(Style::rounded())
312            .with(Header::new(stag.to_string()));
313        let mut table = Table::new([&self.value]);
314        table.with(settings);
315        write!(f, "{table}")
316    }
317}
318
319impl std::fmt::Display for MoveInstance<Address> {
320    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321        write!(f, "{}", self.value)
322    }
323}
324
325macro_rules! impl_primitive_move_instance_display {
326    ($($type:ty)+) => {$(
327        impl std::fmt::Display for MoveInstance<$type> {
328            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
329                write!(f, "{}", self.value)
330            }
331        }
332    )+};
333}
334
335impl_primitive_move_instance_display! {
336    bool
337    u8
338    u16
339    u32
340    u64
341    u128
342    U256
343}
344
345impl<T: MoveType> MoveInstance<T> {
346    /// Convenience function for constructing from raw RPC-returned general types.
347    pub fn from_raw_type(tag: TypeTag, bytes: &[u8]) -> Result<Self, FromRawTypeError> {
348        Ok(Self {
349            type_: tag.try_into()?,
350            value: T::from_bcs(bytes)?,
351        })
352    }
353}
354
355impl<T: MoveStruct> MoveInstance<T> {
356    /// Convenience function for constructing from raw RPC-returned structs.
357    pub fn from_raw_struct(stag: StructTag, bytes: &[u8]) -> Result<Self, FromRawStructError> {
358        Ok(Self {
359            type_: stag.try_into()?,
360            value: T::from_bcs(bytes)?,
361        })
362    }
363}
364
365fn number_to_string_value_recursive(value: &mut serde_json::Value) {
366    match value {
367        serde_json::Value::Array(a) => {
368            for v in a {
369                number_to_string_value_recursive(v)
370            }
371        }
372        serde_json::Value::Number(n) => *value = serde_json::Value::String(n.to_string()),
373        serde_json::Value::Object(o) => {
374            for v in o.values_mut() {
375                number_to_string_value_recursive(v)
376            }
377        }
378        _ => (),
379    }
380}
381
382// =============================================================================
383// Trait impls
384// =============================================================================
385
386macro_rules! impl_primitive_type_tags {
387    ($($typ:ty: ($type_:ident, $variant:ident)),*) => {
388        $(
389            #[derive(
390                Clone,
391                Debug,
392                PartialEq,
393                Eq,
394                Hash,
395                Deserialize,
396                PartialOrd,
397                Ord,
398                Serialize
399            )]
400            pub struct $type_;
401
402            impl From<$type_> for TypeTag {
403                fn from(_value: $type_) -> Self {
404                    Self::$variant
405                }
406            }
407
408            impl TryFrom<TypeTag> for $type_ {
409                type Error = TypeTagError;
410
411                fn try_from(value: TypeTag) -> Result<Self, Self::Error> {
412                    match value {
413                        TypeTag::$variant => Ok(Self),
414                        _ => Err(TypeTagError::Variant {
415                            expected: stringify!($variant).to_owned(),
416                            got: value }
417                        )
418                    }
419                }
420            }
421
422            impl FromStr for $type_ {
423                type Err = ParseTypeTagError;
424
425                fn from_str(s: &str) -> Result<Self, Self::Err> {
426                    let tag: TypeTag = s.parse()?;
427                    Ok(tag.try_into()?)
428                }
429            }
430
431            impl MoveType for $typ {
432                type TypeTag = $type_;
433            }
434
435            impl StaticTypeTag for $typ {
436                fn type_() -> Self::TypeTag {
437                    $type_ {}
438                }
439            }
440        )*
441    };
442}
443
444impl_primitive_type_tags! {
445    Address: (AddressTypeTag, Address),
446    bool: (BoolTypeTag, Bool),
447    u8: (U8TypeTag, U8),
448    u16: (U16TypeTag, U16),
449    u32: (U32TypeTag, U32),
450    u64: (U64TypeTag, U64),
451    u128: (U128TypeTag, U128),
452    U256: (U256TypeTag, U256)
453}
454
455#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, PartialOrd, Ord, Serialize)]
456pub struct StringTypeTag;
457
458impl From<StringTypeTag> for TypeTag {
459    fn from(value: StringTypeTag) -> Self {
460        Self::Struct(Box::new(value.into()))
461    }
462}
463
464impl TryFrom<TypeTag> for StringTypeTag {
465    type Error = TypeTagError;
466
467    fn try_from(value: TypeTag) -> Result<Self, Self::Error> {
468        match value {
469            TypeTag::Struct(stag) => Ok((*stag).try_into()?),
470            other => Err(TypeTagError::Variant {
471                expected: "Struct(_)".to_owned(),
472                got: other,
473            }),
474        }
475    }
476}
477
478impl From<StringTypeTag> for StructTag {
479    fn from(_: StringTypeTag) -> Self {
480        String::struct_tag()
481    }
482}
483
484impl TryFrom<StructTag> for StringTypeTag {
485    type Error = StructTagError;
486
487    fn try_from(value: StructTag) -> Result<Self, Self::Error> {
488        use StructTagError::*;
489        let StructTag {
490            address,
491            module,
492            name,
493            type_params,
494        } = value;
495        let expected = String::struct_tag();
496        if address != expected.address {
497            return Err(Address {
498                expected: expected.address,
499                got: address,
500            });
501        }
502        if module != expected.module {
503            return Err(Module {
504                expected: expected.module,
505                got: module,
506            });
507        }
508        if name != expected.name {
509            return Err(Name {
510                expected: expected.name,
511                got: name,
512            });
513        }
514        if !type_params.is_empty() {
515            return Err(TypeParams(TypeParamsError::Number {
516                expected: 0,
517                got: type_params.len(),
518            }));
519        }
520        Ok(Self)
521    }
522}
523
524impl FromStr for StringTypeTag {
525    type Err = ParseStructTagError;
526
527    fn from_str(s: &str) -> Result<Self, Self::Err> {
528        let stag: StructTag = s.parse()?;
529        Ok(stag.try_into()?)
530    }
531}
532
533impl MoveType for String {
534    type TypeTag = StringTypeTag;
535}
536
537impl MoveStruct for String {
538    type StructTag = StringTypeTag;
539}
540
541impl StaticTypeTag for String {
542    fn type_() -> Self::TypeTag {
543        StringTypeTag {}
544    }
545}
546
547impl StaticAddress for String {
548    fn address() -> Address {
549        Address::new(af_sui_types::hex_address_bytes(b"0x1"))
550    }
551}
552
553impl StaticModule for String {
554    fn module() -> Identifier {
555        IdentStr::cast("string").to_owned()
556    }
557}
558
559impl StaticName for String {
560    fn name() -> Identifier {
561        IdentStr::cast("String").to_owned()
562    }
563}
564
565impl StaticTypeParams for String {
566    fn type_params() -> Vec<TypeTag> {
567        vec![]
568    }
569}