moverox_traits/
lib.rs

1#![cfg_attr(nightly, feature(doc_cfg))]
2
3//! Traits for rusty Move types 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).
9//!
10//! For Move structs, [`MoveDatatype`](crate::MoveDatatype) should be used as it has an
11//! associated [`MoveDatatypeTag`](crate::MoveDatatypeTag). The
12//! [`MoveDatatype`](moverox_traits_derive::MoveDatatype) derive macro is exported for automatically
13//! creating a `MoveDatatypeTag` implementation from normal Rust struct declarations.
14use std::error::Error as StdError;
15use std::fmt::Debug;
16
17#[cfg(feature = "derive")]
18pub use moverox_traits_derive::MoveDatatype;
19use moverox_types::{Address, IdentStr, Identifier, StructTag, TypeTag};
20
21#[cfg(feature = "derive")]
22#[doc(hidden)]
23pub mod external;
24mod primitives;
25mod vector;
26
27pub use self::primitives::{
28    AddressTypeTag,
29    BoolTypeTag,
30    U8TypeTag,
31    U16TypeTag,
32    U32TypeTag,
33    U64TypeTag,
34    U128TypeTag,
35    U256TypeTag,
36};
37pub use self::vector::VecTypeTag;
38
39/// Re-exports all traits in one place, for easy import.
40pub mod prelude {
41    #[doc(inline)]
42    pub use crate::{
43        ConstAddress,
44        ConstModule,
45        ConstName,
46        ConstStructTag,
47        ConstTypeTag,
48        HasKey,
49        MoveDatatype,
50        MoveDatatypeTag,
51        MoveType,
52        MoveTypeTag,
53    };
54}
55
56// =============================================================================
57//  MoveType
58// =============================================================================
59
60/// Marker for a Move type with its associated type tag.
61pub trait MoveType {
62    type TypeTag: MoveTypeTag;
63}
64
65/// A specialized Move type tag, convertible from/to a generic [`TypeTag`] by reference.
66pub trait MoveTypeTag {
67    fn as_datatype_tag(&self) -> Option<&dyn MoveDatatypeTag>;
68
69    fn to_type_tag(&self) -> TypeTag;
70
71    fn from_type_tag(value: &TypeTag) -> Result<Self, TypeTagError>
72    where
73        Self: Sized;
74}
75
76impl<T> MoveTypeTag for T
77where
78    T: MoveDatatypeTag,
79{
80    fn as_datatype_tag(&self) -> Option<&dyn MoveDatatypeTag> {
81        Some(self as _)
82    }
83
84    fn from_type_tag(value: &TypeTag) -> Result<Self, TypeTagError>
85    where
86        Self: Sized,
87    {
88        match value {
89            TypeTag::Struct(stag) => Ok(Self::from_struct_tag(stag)?),
90            other => Err(TypeTagError::Variant {
91                expected: "Struct(_)".to_owned(),
92                got: type_tag_variant_name(other),
93            }),
94        }
95    }
96
97    fn to_type_tag(&self) -> TypeTag {
98        TypeTag::Struct(Box::new(self.to_struct_tag()))
99    }
100}
101
102// =============================================================================
103//  MoveDatatype
104// =============================================================================
105
106/// Marker for a Move datatype with its associated struct tag.
107pub trait MoveDatatype: MoveType<TypeTag = Self::StructTag> {
108    type StructTag: MoveDatatypeTag;
109}
110
111/// A specialized Move type tag for datatypes, convertible from/to a generic [`StructTag`] by
112/// reference.
113pub trait MoveDatatypeTag: MoveTypeTag {
114    fn address(&self) -> Address;
115
116    fn module(&self) -> &IdentStr;
117
118    fn name(&self) -> &IdentStr;
119
120    fn type_params(&self) -> Box<[&dyn MoveTypeTag]>;
121
122    fn to_struct_tag(&self) -> StructTag {
123        StructTag {
124            address: self.address(),
125            module: self.module().to_owned(),
126            name: self.name().to_owned(),
127            type_params: self.type_params().iter().map(|t| t.to_type_tag()).collect(),
128        }
129    }
130
131    fn from_struct_tag(value: &StructTag) -> Result<Self, StructTagError>
132    where
133        Self: Sized;
134}
135
136// =============================================================================
137//  Abilities
138// =============================================================================
139
140/// An oxidized object, i.e., originally a Move type with the `key` ability.
141pub trait HasKey {
142    /// This object's address on-chain.
143    fn address(&self) -> Address;
144}
145
146// =============================================================================
147//  Static attributes
148// =============================================================================
149
150/// Struct tag with a constant address.
151pub trait ConstAddress {
152    const ADDRESS: Address;
153}
154
155/// Struct tag with a constant module.
156pub trait ConstModule {
157    const MODULE: &IdentStr;
158}
159
160/// Struct tag with a constant name.
161pub trait ConstName {
162    const NAME: &IdentStr;
163}
164
165/// [`MoveType`] with a constant type tag.
166pub trait ConstTypeTag: MoveType {
167    const TYPE_TAG: Self::TypeTag;
168}
169
170impl<T> ConstTypeTag for T
171where
172    T: ConstStructTag,
173{
174    const TYPE_TAG: Self::TypeTag = Self::STRUCT_TAG;
175}
176
177/// [`MoveDatatype`] with a constant struct tag.
178pub trait ConstStructTag: MoveDatatype {
179    const STRUCT_TAG: Self::StructTag;
180}
181
182// =============================================================================
183//  Errors used in traits
184// =============================================================================
185
186#[derive(thiserror::Error, Debug)]
187pub enum TypeTagError {
188    #[error("Wrong TypeTag variant: expected {expected}, got {got}")]
189    Variant { expected: String, got: String },
190    #[error("StructTag params: {0}")]
191    StructTag(#[from] StructTagError),
192}
193
194#[derive(thiserror::Error, Debug)]
195pub enum StructTagError {
196    #[error("Wrong address: expected {expected}, got {got}")]
197    Address { expected: Address, got: Address },
198    #[error("Wrong module: expected {expected}, got {got}")]
199    Module {
200        expected: Identifier,
201        got: Identifier,
202    },
203    #[error("Wrong name: expected {expected}, got {got}")]
204    Name {
205        expected: Identifier,
206        got: Identifier,
207    },
208    #[error("Wrong type parameters: {0}")]
209    TypeParams(#[from] TypeParamsError),
210}
211
212#[derive(thiserror::Error, Debug)]
213pub enum TypeParamsError {
214    #[error("Wrong number of generics: expected {expected}, got {got}")]
215    Number { expected: usize, got: usize },
216    #[error("Wrong type for generic: {0}")]
217    TypeTag(Box<TypeTagError>),
218}
219
220impl From<TypeTagError> for TypeParamsError {
221    fn from(value: TypeTagError) -> Self {
222        Self::TypeTag(Box::new(value))
223    }
224}
225
226// =============================================================================
227//  Errors used in derived impls
228// =============================================================================
229
230#[derive(thiserror::Error, Debug)]
231pub enum ParseTypeTagError {
232    #[error("Parsing TypeTag: {0}")]
233    FromStr(Box<dyn StdError + Send + Sync + 'static>),
234    #[error("Converting from TypeTag: {0}")]
235    TypeTag(#[from] TypeTagError),
236}
237
238impl ParseTypeTagError {
239    fn from_str(err: impl StdError + Send + Sync + 'static) -> Self {
240        Self::FromStr(err.into())
241    }
242}
243
244#[derive(thiserror::Error, Debug)]
245pub enum ParseStructTagError {
246    #[error("Parsing StructTag: {0}")]
247    FromStr(Box<dyn StdError + Send + Sync + 'static>),
248    #[error("Converting from StructTag: {0}")]
249    StructTag(#[from] StructTagError),
250}
251
252// =============================================================================
253//  Internals
254// =============================================================================
255
256fn type_tag_variant_name(this: &TypeTag) -> String {
257    match this {
258        TypeTag::U8 => "U8",
259        TypeTag::U16 => "U16",
260        TypeTag::U32 => "U32",
261        TypeTag::U64 => "U64",
262        TypeTag::U128 => "U128",
263        TypeTag::U256 => "U256",
264        TypeTag::Bool => "Bool",
265        TypeTag::Address => "Address",
266        TypeTag::Signer => "Signer",
267        TypeTag::Vector(_) => "Vector",
268        TypeTag::Struct(_) => "Struct",
269    }
270    .to_owned()
271}