1#![cfg_attr(all(doc, not(doctest)), feature(doc_cfg))]
2
3use 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, Identifier, StructTag, TypeTag};
24use serde::{Deserialize, Serialize};
25
26#[doc(hidden)]
27pub mod external;
28pub mod otw;
29mod primitives;
30mod string;
31pub mod vector;
32
33pub use self::primitives::{
34 AddressTypeTag,
35 BoolTypeTag,
36 U8TypeTag,
37 U16TypeTag,
38 U32TypeTag,
39 U64TypeTag,
40 U128TypeTag,
41 U256TypeTag,
42};
43pub use self::string::StringTypeTag;
44
45pub trait MoveType:
51 Clone
52 + std::fmt::Debug
53 + std::fmt::Display
54 + for<'de> Deserialize<'de>
55 + Serialize
56 + PartialEq
57 + Eq
58 + std::hash::Hash
59{
60 type TypeTag: MoveTypeTag;
61
62 fn from_bcs(bytes: &[u8]) -> bcs::Result<Self> {
64 bcs::from_bytes(bytes)
65 }
66
67 fn into_bcs(self) -> bcs::Result<Vec<u8>> {
69 bcs::to_bytes(&self)
70 }
71
72 fn to_bcs(&self) -> bcs::Result<Vec<u8>> {
74 bcs::to_bytes(self)
75 }
76
77 fn into_json(self) -> serde_json::Value {
79 let mut value = serde_json::json!(self);
80 number_to_string_value_recursive(&mut value);
82 value
83 }
84
85 fn to_json(&self) -> serde_json::Value {
92 let mut value = serde_json::json!(self);
93 number_to_string_value_recursive(&mut value);
95 value
96 }
97}
98
99pub trait MoveTypeTag:
100 Into<TypeTag>
101 + TryFrom<TypeTag, Error = TypeTagError>
102 + FromStr
103 + Clone
104 + Debug
105 + PartialEq
106 + Eq
107 + Hash
108 + for<'de> Deserialize<'de>
109 + PartialOrd
110 + Ord
111 + Serialize
112{
113}
114
115impl<T> MoveTypeTag for T where
116 T: Into<TypeTag>
117 + TryFrom<TypeTag, Error = TypeTagError>
118 + FromStr
119 + Clone
120 + Debug
121 + PartialEq
122 + Eq
123 + Hash
124 + for<'de> Deserialize<'de>
125 + PartialOrd
126 + Ord
127 + Serialize
128{
129}
130
131pub trait MoveStruct: MoveType<TypeTag = Self::StructTag> {
137 type StructTag: MoveStructTag;
138}
139
140pub trait MoveStructTag:
141 Into<StructTag> + TryFrom<StructTag, Error = StructTagError> + MoveTypeTag
142{
143}
144
145impl<T> MoveStructTag for T where
146 T: Into<StructTag> + TryFrom<StructTag, Error = StructTagError> + MoveTypeTag
147{
148}
149
150pub trait HasKey: MoveStruct {
155 fn object_id(&self) -> Address;
156}
157
158pub trait HasCopy: MoveStruct + Copy {}
159
160pub trait HasStore: MoveStruct {}
161
162pub trait HasDrop: MoveStruct {}
163
164pub trait StaticTypeTag: MoveType {
170 fn type_() -> Self::TypeTag;
171
172 fn type_tag() -> TypeTag {
173 Self::type_().into()
174 }
175}
176
177pub trait StaticAddress: MoveStruct {
179 fn address() -> Address;
180}
181
182pub trait StaticModule: MoveStruct {
184 fn module() -> Identifier;
185}
186
187pub trait StaticName: MoveStruct {
189 fn name() -> Identifier;
190}
191
192pub trait StaticTypeParams: MoveStruct {
194 fn type_params() -> Vec<TypeTag>;
195}
196
197pub trait StaticStructTag: MoveStruct {
199 fn struct_tag() -> StructTag;
200}
201
202impl<T> StaticStructTag for T
203where
204 T: StaticAddress + StaticModule + StaticName + StaticTypeParams,
205{
206 fn struct_tag() -> StructTag {
207 StructTag {
208 address: Self::address(),
209 module: Self::module(),
210 name: Self::name(),
211 type_params: Self::type_params(),
212 }
213 }
214}
215
216#[derive(Clone, Debug, PartialEq, Eq, Hash)]
225pub struct MoveInstance<T: MoveType> {
226 pub type_: T::TypeTag,
227 pub value: T,
228}
229
230impl<T: StaticTypeTag> From<T> for MoveInstance<T> {
231 fn from(value: T) -> Self {
232 Self {
233 type_: T::type_(),
234 value,
235 }
236 }
237}
238
239impl<T: MoveStruct + tabled::Tabled> std::fmt::Display for MoveInstance<T> {
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 use tabled::Table;
242 use tabled::settings::panel::Header;
243 use tabled::settings::{Rotate, Settings, Style};
244
245 let stag: StructTag = self.type_.clone().into();
246 let settings = Settings::default()
247 .with(Rotate::Left)
248 .with(Rotate::Top)
249 .with(Style::rounded())
250 .with(Header::new(stag.to_string()));
251 let mut table = Table::new([&self.value]);
252 table.with(settings);
253 write!(f, "{table}")
254 }
255}
256
257impl std::fmt::Display for MoveInstance<Address> {
258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259 write!(f, "{}", self.value)
260 }
261}
262
263macro_rules! impl_primitive_move_instance_display {
264 ($($type:ty)+) => {$(
265 impl std::fmt::Display for MoveInstance<$type> {
266 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267 write!(f, "{}", self.value)
268 }
269 }
270 )+};
271}
272
273impl_primitive_move_instance_display! {
274 bool
275 u8
276 u16
277 u32
278 u64
279 u128
280 U256
281}
282
283impl<T: MoveType> MoveInstance<T> {
284 pub fn from_raw_type(tag: TypeTag, bytes: &[u8]) -> Result<Self, FromRawTypeError> {
286 Ok(Self {
287 type_: tag.try_into()?,
288 value: T::from_bcs(bytes)?,
289 })
290 }
291}
292
293impl<T: MoveStruct> MoveInstance<T> {
294 pub fn from_raw_struct(stag: StructTag, bytes: &[u8]) -> Result<Self, FromRawStructError> {
296 Ok(Self {
297 type_: stag.try_into()?,
298 value: T::from_bcs(bytes)?,
299 })
300 }
301}
302
303fn number_to_string_value_recursive(value: &mut serde_json::Value) {
304 match value {
305 serde_json::Value::Array(a) => {
306 for v in a {
307 number_to_string_value_recursive(v)
308 }
309 }
310 serde_json::Value::Number(n) => *value = serde_json::Value::String(n.to_string()),
311 serde_json::Value::Object(o) => {
312 for v in o.values_mut() {
313 number_to_string_value_recursive(v)
314 }
315 }
316 _ => (),
317 }
318}
319
320pub trait ObjectExt {
326 fn struct_instance<T: MoveStruct>(&self) -> Result<MoveInstance<T>, ObjectError>;
328}
329
330impl ObjectExt for sui_sdk_types::Object {
331 fn struct_instance<T: MoveStruct>(&self) -> Result<MoveInstance<T>, ObjectError> {
332 let sui_sdk_types::ObjectData::Struct(s) = self.data() else {
333 return Err(ObjectError::NotStruct);
334 };
335 MoveInstance::from_raw_struct(s.object_type().clone(), s.contents()).map_err(From::from)
336 }
337}
338
339#[derive(thiserror::Error, Debug)]
341pub enum ObjectError {
342 #[error("Object is not a Move struct")]
343 NotStruct,
344 #[error(transparent)]
345 FromRawStruct(#[from] FromRawStructError),
346}
347
348#[derive(thiserror::Error, Debug)]
353pub enum TypeTagError {
354 #[error("Wrong TypeTag variant: expected {expected}, got {got}")]
355 Variant { expected: String, got: TypeTag },
356 #[error("StructTag params: {0}")]
357 StructTag(#[from] StructTagError),
358}
359
360#[derive(thiserror::Error, Debug)]
361pub enum StructTagError {
362 #[error("Wrong address: expected {expected}, got {got}")]
363 Address { expected: Address, got: Address },
364 #[error("Wrong module: expected {expected}, got {got}")]
365 Module {
366 expected: Identifier,
367 got: Identifier,
368 },
369 #[error("Wrong name: expected {expected}, got {got}")]
370 Name {
371 expected: Identifier,
372 got: Identifier,
373 },
374 #[error("Wrong type parameters: {0}")]
375 TypeParams(#[from] TypeParamsError),
376}
377
378#[derive(thiserror::Error, Debug)]
379pub enum TypeParamsError {
380 #[error("Wrong number of generics: expected {expected}, got {got}")]
381 Number { expected: usize, got: usize },
382 #[error("Wrong type for generic: {0}")]
383 TypeTag(Box<TypeTagError>),
384}
385
386impl From<TypeTagError> for TypeParamsError {
387 fn from(value: TypeTagError) -> Self {
388 Self::TypeTag(Box::new(value))
389 }
390}
391
392#[derive(thiserror::Error, Debug)]
393pub enum ParseTypeTagError {
394 #[error("Parsing TypeTag: {0}")]
395 FromStr(#[from] sui_sdk_types::TypeParseError),
396 #[error("Converting from TypeTag: {0}")]
397 TypeTag(#[from] TypeTagError),
398}
399
400#[derive(thiserror::Error, Debug)]
401pub enum ParseStructTagError {
402 #[error("Parsing StructTag: {0}")]
403 FromStr(#[from] sui_sdk_types::TypeParseError),
404 #[error("Converting from StructTag: {0}")]
405 StructTag(#[from] StructTagError),
406}
407
408#[derive(thiserror::Error, Debug)]
409pub enum FromRawTypeError {
410 #[error("Converting from TypeTag: {0}")]
411 TypeTag(#[from] TypeTagError),
412 #[error("Deserializing BCS: {0}")]
413 Bcs(#[from] bcs::Error),
414}
415
416#[derive(thiserror::Error, Debug)]
417pub enum FromRawStructError {
418 #[error("Converting from StructTag: {0}")]
419 StructTag(#[from] StructTagError),
420 #[error("Deserializing BCS: {0}")]
421 Bcs(#[from] bcs::Error),
422}