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, Identifier, ObjectId, 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) -> ObjectId;
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 af_sui_types::Object {
331 fn struct_instance<T: MoveStruct>(&self) -> Result<MoveInstance<T>, ObjectError> {
332 let _struct = self.as_move().ok_or(ObjectError::NotStruct)?;
333 MoveInstance::from_raw_struct(_struct.type_.clone().into(), &_struct.contents)
334 .map_err(From::from)
335 }
336}
337
338impl ObjectExt for sui_sdk_types::Object {
339 fn struct_instance<T: MoveStruct>(&self) -> Result<MoveInstance<T>, ObjectError> {
340 let sui_sdk_types::ObjectData::Struct(s) = self.data() else {
341 return Err(ObjectError::NotStruct);
342 };
343 MoveInstance::from_raw_struct(s.object_type().clone(), s.contents()).map_err(From::from)
344 }
345}
346
347#[derive(thiserror::Error, Debug)]
349pub enum ObjectError {
350 #[error("Object is not a Move struct")]
351 NotStruct,
352 #[error(transparent)]
353 FromRawStruct(#[from] FromRawStructError),
354}
355
356#[derive(thiserror::Error, Debug)]
361pub enum TypeTagError {
362 #[error("Wrong TypeTag variant: expected {expected}, got {got}")]
363 Variant { expected: String, got: TypeTag },
364 #[error("StructTag params: {0}")]
365 StructTag(#[from] StructTagError),
366}
367
368#[derive(thiserror::Error, Debug)]
369pub enum StructTagError {
370 #[error("Wrong address: expected {expected}, got {got}")]
371 Address { expected: Address, got: Address },
372 #[error("Wrong module: expected {expected}, got {got}")]
373 Module {
374 expected: Identifier,
375 got: Identifier,
376 },
377 #[error("Wrong name: expected {expected}, got {got}")]
378 Name {
379 expected: Identifier,
380 got: Identifier,
381 },
382 #[error("Wrong type parameters: {0}")]
383 TypeParams(#[from] TypeParamsError),
384}
385
386#[derive(thiserror::Error, Debug)]
387pub enum TypeParamsError {
388 #[error("Wrong number of generics: expected {expected}, got {got}")]
389 Number { expected: usize, got: usize },
390 #[error("Wrong type for generic: {0}")]
391 TypeTag(Box<TypeTagError>),
392}
393
394impl From<TypeTagError> for TypeParamsError {
395 fn from(value: TypeTagError) -> Self {
396 Self::TypeTag(Box::new(value))
397 }
398}
399
400#[derive(thiserror::Error, Debug)]
401pub enum ParseTypeTagError {
402 #[error("Parsing TypeTag: {0}")]
403 FromStr(#[from] sui_sdk_types::TypeParseError),
404 #[error("Converting from TypeTag: {0}")]
405 TypeTag(#[from] TypeTagError),
406}
407
408#[derive(thiserror::Error, Debug)]
409pub enum ParseStructTagError {
410 #[error("Parsing StructTag: {0}")]
411 FromStr(#[from] sui_sdk_types::TypeParseError),
412 #[error("Converting from StructTag: {0}")]
413 StructTag(#[from] StructTagError),
414}
415
416#[derive(thiserror::Error, Debug)]
417pub enum FromRawTypeError {
418 #[error("Converting from TypeTag: {0}")]
419 TypeTag(#[from] TypeTagError),
420 #[error("Deserializing BCS: {0}")]
421 Bcs(#[from] bcs::Error),
422}
423
424#[derive(thiserror::Error, Debug)]
425pub enum FromRawStructError {
426 #[error("Converting from StructTag: {0}")]
427 StructTag(#[from] StructTagError),
428 #[error("Deserializing BCS: {0}")]
429 Bcs(#[from] bcs::Error),
430}