1#![cfg_attr(all(doc, not(doctest)), feature(doc_auto_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, IdentStr, Identifier, ObjectId, StructTag, TypeTag};
24use serde::{Deserialize, Serialize};
25
26#[doc(hidden)]
27pub mod external;
28pub mod otw;
29pub mod vector;
30
31#[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
107pub 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 fn from_bcs(bytes: &[u8]) -> bcs::Result<Self> {
126 bcs::from_bytes(bytes)
127 }
128
129 fn into_bcs(self) -> bcs::Result<Vec<u8>> {
131 bcs::to_bytes(&self)
132 }
133
134 fn to_bcs(&self) -> bcs::Result<Vec<u8>> {
136 bcs::to_bytes(self)
137 }
138
139 fn into_json(self) -> serde_json::Value {
141 let mut value = serde_json::json!(self);
142 number_to_string_value_recursive(&mut value);
144 value
145 }
146
147 fn to_json(&self) -> serde_json::Value {
154 let mut value = serde_json::json!(self);
155 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
193pub 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
212pub 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
226pub trait StaticTypeTag: MoveType {
232 fn type_() -> Self::TypeTag;
233
234 fn type_tag() -> TypeTag {
235 Self::type_().into()
236 }
237}
238
239pub trait StaticAddress: MoveStruct {
241 fn address() -> Address;
242}
243
244pub trait StaticModule: MoveStruct {
246 fn module() -> Identifier;
247}
248
249pub trait StaticName: MoveStruct {
251 fn name() -> Identifier;
252}
253
254pub trait StaticTypeParams: MoveStruct {
256 fn type_params() -> Vec<TypeTag>;
257}
258
259pub 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#[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::Table;
304 use tabled::settings::panel::Header;
305 use tabled::settings::{Rotate, Settings, Style};
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 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 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#[derive(thiserror::Error, Debug)]
384pub enum ObjectError {
385 #[error("Object is not a Move struct")]
386 NotStruct,
387 #[error(transparent)]
388 FromRawStruct(#[from] FromRawStructError),
389}
390
391pub trait ObjectExt {
393 fn struct_instance<T: MoveStruct>(&self) -> Result<MoveInstance<T>, ObjectError>;
395}
396
397impl ObjectExt for af_sui_types::Object {
398 fn struct_instance<T: MoveStruct>(&self) -> Result<MoveInstance<T>, ObjectError> {
399 let _struct = self.as_move().ok_or(ObjectError::NotStruct)?;
400 MoveInstance::from_raw_struct(_struct.type_.clone().into(), &_struct.contents)
401 .map_err(From::from)
402 }
403}
404
405impl ObjectExt for sui_sdk_types::Object {
406 fn struct_instance<T: MoveStruct>(&self) -> Result<MoveInstance<T>, ObjectError> {
407 let sui_sdk_types::ObjectData::Struct(s) = self.data() else {
408 return Err(ObjectError::NotStruct);
409 };
410 MoveInstance::from_raw_struct(s.object_type().clone(), s.contents()).map_err(From::from)
411 }
412}
413
414macro_rules! impl_primitive_type_tags {
419 ($($typ:ty: ($type_:ident, $variant:ident)),*) => {
420 $(
421 #[derive(
422 Clone,
423 Debug,
424 PartialEq,
425 Eq,
426 Hash,
427 Deserialize,
428 PartialOrd,
429 Ord,
430 Serialize
431 )]
432 pub struct $type_;
433
434 impl From<$type_> for TypeTag {
435 fn from(_value: $type_) -> Self {
436 Self::$variant
437 }
438 }
439
440 impl TryFrom<TypeTag> for $type_ {
441 type Error = TypeTagError;
442
443 fn try_from(value: TypeTag) -> Result<Self, Self::Error> {
444 match value {
445 TypeTag::$variant => Ok(Self),
446 _ => Err(TypeTagError::Variant {
447 expected: stringify!($variant).to_owned(),
448 got: value }
449 )
450 }
451 }
452 }
453
454 impl FromStr for $type_ {
455 type Err = ParseTypeTagError;
456
457 fn from_str(s: &str) -> Result<Self, Self::Err> {
458 let tag: TypeTag = s.parse()?;
459 Ok(tag.try_into()?)
460 }
461 }
462
463 impl MoveType for $typ {
464 type TypeTag = $type_;
465 }
466
467 impl StaticTypeTag for $typ {
468 fn type_() -> Self::TypeTag {
469 $type_ {}
470 }
471 }
472 )*
473 };
474}
475
476impl_primitive_type_tags! {
477 Address: (AddressTypeTag, Address),
478 bool: (BoolTypeTag, Bool),
479 u8: (U8TypeTag, U8),
480 u16: (U16TypeTag, U16),
481 u32: (U32TypeTag, U32),
482 u64: (U64TypeTag, U64),
483 u128: (U128TypeTag, U128),
484 U256: (U256TypeTag, U256)
485}
486
487#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, PartialOrd, Ord, Serialize)]
488pub struct StringTypeTag;
489
490impl From<StringTypeTag> for TypeTag {
491 fn from(value: StringTypeTag) -> Self {
492 Self::Struct(Box::new(value.into()))
493 }
494}
495
496impl TryFrom<TypeTag> for StringTypeTag {
497 type Error = TypeTagError;
498
499 fn try_from(value: TypeTag) -> Result<Self, Self::Error> {
500 match value {
501 TypeTag::Struct(stag) => Ok((*stag).try_into()?),
502 other => Err(TypeTagError::Variant {
503 expected: "Struct(_)".to_owned(),
504 got: other,
505 }),
506 }
507 }
508}
509
510impl From<StringTypeTag> for StructTag {
511 fn from(_: StringTypeTag) -> Self {
512 String::struct_tag()
513 }
514}
515
516impl TryFrom<StructTag> for StringTypeTag {
517 type Error = StructTagError;
518
519 fn try_from(value: StructTag) -> Result<Self, Self::Error> {
520 use StructTagError::*;
521 let StructTag {
522 address,
523 module,
524 name,
525 type_params,
526 } = value;
527 let expected = String::struct_tag();
528 if address != expected.address {
529 return Err(Address {
530 expected: expected.address,
531 got: address,
532 });
533 }
534 if module != expected.module {
535 return Err(Module {
536 expected: expected.module,
537 got: module,
538 });
539 }
540 if name != expected.name {
541 return Err(Name {
542 expected: expected.name,
543 got: name,
544 });
545 }
546 if !type_params.is_empty() {
547 return Err(TypeParams(TypeParamsError::Number {
548 expected: 0,
549 got: type_params.len(),
550 }));
551 }
552 Ok(Self)
553 }
554}
555
556impl FromStr for StringTypeTag {
557 type Err = ParseStructTagError;
558
559 fn from_str(s: &str) -> Result<Self, Self::Err> {
560 let stag: StructTag = s.parse()?;
561 Ok(stag.try_into()?)
562 }
563}
564
565impl MoveType for String {
566 type TypeTag = StringTypeTag;
567}
568
569impl MoveStruct for String {
570 type StructTag = StringTypeTag;
571}
572
573impl StaticTypeTag for String {
574 fn type_() -> Self::TypeTag {
575 StringTypeTag {}
576 }
577}
578
579impl StaticAddress for String {
580 fn address() -> Address {
581 Address::new(af_sui_types::hex_address_bytes(b"0x1"))
582 }
583}
584
585impl StaticModule for String {
586 fn module() -> Identifier {
587 IdentStr::cast("string").to_owned()
588 }
589}
590
591impl StaticName for String {
592 fn name() -> Identifier {
593 IdentStr::cast("String").to_owned()
594 }
595}
596
597impl StaticTypeParams for String {
598 fn type_params() -> Vec<TypeTag> {
599 vec![]
600 }
601}