1use crate::error::{AptosError, AptosResult};
12use crate::types::AccountAddress;
13use serde::{Deserialize, Serialize};
14use std::fmt;
15use std::str::FromStr;
16
17const MAX_TYPE_TAG_LENGTH: usize = 1024;
20
21const MAX_IDENTIFIER_LENGTH: usize = 128;
23
24const MAX_TYPE_NESTING_DEPTH: usize = 8;
26
27#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
32#[serde(transparent)]
33pub struct Identifier(String);
34
35impl Identifier {
36 pub fn new(s: impl Into<String>) -> AptosResult<Self> {
49 let s = s.into();
50 if s.len() > MAX_IDENTIFIER_LENGTH {
52 return Err(AptosError::InvalidTypeTag(format!(
53 "identifier too long: {} bytes (max {})",
54 s.len(),
55 MAX_IDENTIFIER_LENGTH
56 )));
57 }
58 let maybe_first = s.chars().next();
59 let Some(first) = maybe_first else {
60 return Err(AptosError::InvalidTypeTag(
61 "identifier cannot be empty".into(),
62 ));
63 };
64
65 if !first.is_ascii_alphabetic() && first != '_' {
66 return Err(AptosError::InvalidTypeTag(format!(
67 "identifier must start with letter or underscore: {s}"
68 )));
69 }
70 if !s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
71 return Err(AptosError::InvalidTypeTag(format!(
72 "identifier contains invalid characters: {s}"
73 )));
74 }
75 Ok(Self(s))
76 }
77
78 pub(crate) fn from_string_unchecked(s: String) -> Self {
80 Self(s)
81 }
82
83 pub fn as_str(&self) -> &str {
85 &self.0
86 }
87}
88
89impl fmt::Display for Identifier {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 write!(f, "{}", self.0)
92 }
93}
94
95impl FromStr for Identifier {
96 type Err = AptosError;
97
98 fn from_str(s: &str) -> Result<Self, Self::Err> {
99 Self::new(s)
100 }
101}
102
103#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
105pub struct MoveModuleId {
106 pub address: AccountAddress,
108 pub name: Identifier,
110}
111
112impl MoveModuleId {
113 pub fn new(address: AccountAddress, name: Identifier) -> Self {
115 Self { address, name }
116 }
117
118 pub fn from_str_strict(s: &str) -> AptosResult<Self> {
125 let parts: Vec<&str> = s.split("::").collect();
126 if parts.len() != 2 {
127 return Err(AptosError::InvalidTypeTag(format!(
128 "invalid module ID format: {s}"
129 )));
130 }
131 let address = AccountAddress::from_str(parts[0])?;
132 let name = Identifier::new(parts[1])?;
133 Ok(Self { address, name })
134 }
135}
136
137impl fmt::Display for MoveModuleId {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 write!(f, "{}::{}", self.address.to_short_string(), self.name)
140 }
141}
142
143impl FromStr for MoveModuleId {
144 type Err = AptosError;
145
146 fn from_str(s: &str) -> Result<Self, Self::Err> {
147 Self::from_str_strict(s)
148 }
149}
150
151#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
155pub struct StructTag {
156 pub address: AccountAddress,
158 pub module: Identifier,
160 pub name: Identifier,
162 #[serde(default)]
164 pub type_args: Vec<TypeTag>,
165}
166
167impl StructTag {
168 pub fn new(
170 address: AccountAddress,
171 module: Identifier,
172 name: Identifier,
173 type_args: Vec<TypeTag>,
174 ) -> Self {
175 Self {
176 address,
177 module,
178 name,
179 type_args,
180 }
181 }
182
183 pub fn simple(
189 address: AccountAddress,
190 module: impl Into<String>,
191 name: impl Into<String>,
192 ) -> AptosResult<Self> {
193 Ok(Self {
194 address,
195 module: Identifier::new(module)?,
196 name: Identifier::new(name)?,
197 type_args: vec![],
198 })
199 }
200
201 pub fn aptos_coin() -> Self {
203 Self {
204 address: AccountAddress::ONE,
205 module: Identifier::from_string_unchecked("aptos_coin".to_string()),
206 name: Identifier::from_string_unchecked("AptosCoin".to_string()),
207 type_args: vec![],
208 }
209 }
210}
211
212impl fmt::Display for StructTag {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 write!(
215 f,
216 "{}::{}::{}",
217 self.address.to_short_string(),
218 self.module,
219 self.name
220 )?;
221 if !self.type_args.is_empty() {
222 write!(f, "<")?;
223 for (i, arg) in self.type_args.iter().enumerate() {
224 if i > 0 {
225 write!(f, ", ")?;
226 }
227 write!(f, "{arg}")?;
228 }
229 write!(f, ">")?;
230 }
231 Ok(())
232 }
233}
234
235pub type MoveStructTag = StructTag;
237
238#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
256#[serde(rename_all = "lowercase")]
257pub enum TypeTag {
258 Bool,
260 U8,
262 U64,
264 U128,
266 Address,
268 Signer,
270 Vector(Box<TypeTag>),
272 Struct(Box<StructTag>),
274 U16,
276 U32,
278 U256,
280 I8,
290 I16,
292 I32,
294 I64,
296 I128,
298 I256,
300}
301
302impl TypeTag {
303 pub fn vector(element: TypeTag) -> Self {
305 Self::Vector(Box::new(element))
306 }
307
308 pub fn struct_tag(tag: StructTag) -> Self {
310 Self::Struct(Box::new(tag))
311 }
312
313 pub fn aptos_coin() -> Self {
315 Self::Struct(Box::new(StructTag::aptos_coin()))
316 }
317
318 pub fn from_str_strict(s: &str) -> AptosResult<Self> {
347 let s = s.trim();
348
349 if s.len() > MAX_TYPE_TAG_LENGTH {
351 return Err(AptosError::InvalidTypeTag(format!(
352 "type tag too long: {} bytes (max {})",
353 s.len(),
354 MAX_TYPE_TAG_LENGTH
355 )));
356 }
357
358 Self::parse_type_tag_with_depth(s, 0)
359 }
360
361 fn parse_type_tag_with_depth(s: &str, depth: usize) -> AptosResult<Self> {
363 if depth > MAX_TYPE_NESTING_DEPTH {
365 return Err(AptosError::InvalidTypeTag(format!(
366 "type tag nesting too deep: {depth} levels (max {MAX_TYPE_NESTING_DEPTH})"
367 )));
368 }
369
370 match s {
372 "bool" => return Ok(TypeTag::Bool),
373 "u8" => return Ok(TypeTag::U8),
374 "u16" => return Ok(TypeTag::U16),
375 "u32" => return Ok(TypeTag::U32),
376 "u64" => return Ok(TypeTag::U64),
377 "u128" => return Ok(TypeTag::U128),
378 "u256" => return Ok(TypeTag::U256),
379 "i8" => return Ok(TypeTag::I8),
380 "i16" => return Ok(TypeTag::I16),
381 "i32" => return Ok(TypeTag::I32),
382 "i64" => return Ok(TypeTag::I64),
383 "i128" => return Ok(TypeTag::I128),
384 "i256" => return Ok(TypeTag::I256),
385 "address" => return Ok(TypeTag::Address),
386 "signer" => return Ok(TypeTag::Signer),
387 _ => {}
388 }
389
390 if s.starts_with("vector<") && s.ends_with('>') {
393 let inner = s[7..s.len() - 1].trim();
394 let inner_tag = Self::parse_type_tag_with_depth(inner, depth + 1)?;
395 return Ok(TypeTag::Vector(Box::new(inner_tag)));
396 }
397
398 Self::parse_struct_type_with_depth(s, depth)
400 }
401
402 fn parse_struct_type_with_depth(s: &str, depth: usize) -> AptosResult<Self> {
404 let generic_start = s.find('<');
406
407 let (base, type_args_str) = if let Some(idx) = generic_start {
408 if !s.ends_with('>') {
409 return Err(AptosError::InvalidTypeTag(format!(
410 "malformed generic type: {s}"
411 )));
412 }
413 (&s[..idx], Some(&s[idx + 1..s.len() - 1]))
414 } else {
415 (s, None)
416 };
417
418 let parts: Vec<&str> = base.split("::").collect();
420 if parts.len() != 3 {
421 return Err(AptosError::InvalidTypeTag(format!(
422 "invalid struct type format (expected address::module::name): {s}"
423 )));
424 }
425
426 let address = AccountAddress::from_str(parts[0])?;
427 let module = Identifier::new(parts[1])?;
428 let name = Identifier::new(parts[2])?;
429
430 let type_args = if let Some(args_str) = type_args_str {
432 Self::parse_type_args_with_depth(args_str, depth)?
433 } else {
434 vec![]
435 };
436
437 Ok(TypeTag::Struct(Box::new(StructTag {
438 address,
439 module,
440 name,
441 type_args,
442 })))
443 }
444
445 fn parse_type_args_with_depth(s: &str, depth: usize) -> AptosResult<Vec<TypeTag>> {
453 let mut result = Vec::new();
454 let mut bracket_depth: i32 = 0;
455 let mut start = 0;
456
457 let push_arg = |arg: &str, result: &mut Vec<TypeTag>| -> AptosResult<()> {
459 let arg = arg.trim();
460 if arg.is_empty() {
461 return Err(AptosError::InvalidTypeTag(
462 "empty type argument in generic type".into(),
463 ));
464 }
465 result.push(Self::parse_type_tag_with_depth(arg, depth + 1)?);
466 Ok(())
467 };
468
469 for (i, c) in s.char_indices() {
470 match c {
471 '<' => bracket_depth += 1,
472 '>' => bracket_depth -= 1,
473 ',' if bracket_depth == 0 => {
474 push_arg(&s[start..i], &mut result)?;
475 start = i + 1;
476 }
477 _ => {}
478 }
479 }
480
481 push_arg(&s[start..], &mut result)?;
483
484 Ok(result)
485 }
486}
487
488impl fmt::Display for TypeTag {
489 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490 match self {
491 TypeTag::Bool => write!(f, "bool"),
492 TypeTag::U8 => write!(f, "u8"),
493 TypeTag::U16 => write!(f, "u16"),
494 TypeTag::U32 => write!(f, "u32"),
495 TypeTag::U64 => write!(f, "u64"),
496 TypeTag::U128 => write!(f, "u128"),
497 TypeTag::U256 => write!(f, "u256"),
498 TypeTag::I8 => write!(f, "i8"),
499 TypeTag::I16 => write!(f, "i16"),
500 TypeTag::I32 => write!(f, "i32"),
501 TypeTag::I64 => write!(f, "i64"),
502 TypeTag::I128 => write!(f, "i128"),
503 TypeTag::I256 => write!(f, "i256"),
504 TypeTag::Address => write!(f, "address"),
505 TypeTag::Signer => write!(f, "signer"),
506 TypeTag::Vector(inner) => write!(f, "vector<{inner}>"),
507 TypeTag::Struct(tag) => write!(f, "{tag}"),
508 }
509 }
510}
511
512#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
514pub struct EntryFunctionId {
515 pub module: MoveModuleId,
517 pub name: Identifier,
519}
520
521impl EntryFunctionId {
522 pub fn new(module: MoveModuleId, name: Identifier) -> Self {
524 Self { module, name }
525 }
526
527 pub fn from_str_strict(s: &str) -> AptosResult<Self> {
534 let parts: Vec<&str> = s.split("::").collect();
535 if parts.len() != 3 {
536 return Err(AptosError::InvalidTypeTag(format!(
537 "invalid entry function ID format: {s}"
538 )));
539 }
540 let address = AccountAddress::from_str(parts[0])?;
541 let module = Identifier::new(parts[1])?;
542 let name = Identifier::new(parts[2])?;
543 Ok(Self {
544 module: MoveModuleId::new(address, module),
545 name,
546 })
547 }
548}
549
550impl fmt::Display for EntryFunctionId {
551 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
552 write!(f, "{}::{}", self.module, self.name)
553 }
554}
555
556impl FromStr for EntryFunctionId {
557 type Err = AptosError;
558
559 fn from_str(s: &str) -> Result<Self, Self::Err> {
560 Self::from_str_strict(s)
561 }
562}
563
564#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
566pub struct MoveType(String);
567
568impl MoveType {
569 pub fn new(s: impl Into<String>) -> Self {
571 Self(s.into())
572 }
573
574 pub fn as_str(&self) -> &str {
576 &self.0
577 }
578}
579
580impl fmt::Display for MoveType {
581 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
582 write!(f, "{}", self.0)
583 }
584}
585
586#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
588pub struct MoveResource {
589 #[serde(rename = "type")]
591 pub typ: String,
592 pub data: serde_json::Value,
594}
595
596#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
598pub struct MoveStruct {
599 #[serde(flatten)]
601 pub fields: serde_json::Map<String, serde_json::Value>,
602}
603
604#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
606#[serde(untagged)]
607pub enum MoveValue {
608 Bool(bool),
610 Number(String),
612 String(String),
614 Address(AccountAddress),
616 Vector(Vec<MoveValue>),
618 Struct(MoveStruct),
620 Null,
622}
623
624impl MoveValue {
625 pub fn as_bool(&self) -> Option<bool> {
627 match self {
628 MoveValue::Bool(b) => Some(*b),
629 _ => None,
630 }
631 }
632
633 pub fn as_u64(&self) -> Option<u64> {
635 match self {
636 MoveValue::Number(s) => s.parse().ok(),
637 _ => None,
638 }
639 }
640
641 pub fn as_u128(&self) -> Option<u128> {
643 match self {
644 MoveValue::Number(s) => s.parse().ok(),
645 _ => None,
646 }
647 }
648
649 pub fn as_str(&self) -> Option<&str> {
651 match self {
652 MoveValue::String(s) | MoveValue::Number(s) => Some(s),
653 _ => None,
654 }
655 }
656
657 pub fn as_address(&self) -> Option<&AccountAddress> {
659 match self {
660 MoveValue::Address(a) => Some(a),
661 _ => None,
662 }
663 }
664
665 pub fn as_vec(&self) -> Option<&[MoveValue]> {
667 match self {
668 MoveValue::Vector(v) => Some(v),
669 _ => None,
670 }
671 }
672}
673
674#[cfg(test)]
675mod tests {
676 use super::*;
677
678 #[test]
679 fn test_identifier() {
680 assert!(Identifier::new("hello").is_ok());
681 assert!(Identifier::new("_private").is_ok());
682 assert!(Identifier::new("CamelCase123").is_ok());
683 assert!(Identifier::new("").is_err());
684 assert!(Identifier::new("123start").is_err());
685 assert!(Identifier::new("has-dash").is_err());
686 }
687
688 #[test]
689 fn test_identifier_as_str() {
690 let id = Identifier::new("test").unwrap();
691 assert_eq!(id.as_str(), "test");
692 }
693
694 #[test]
695 fn test_identifier_display() {
696 let id = Identifier::new("my_func").unwrap();
697 assert_eq!(format!("{id}"), "my_func");
698 }
699
700 #[test]
701 fn test_module_id() {
702 let module_id = MoveModuleId::from_str_strict("0x1::coin").unwrap();
703 assert_eq!(module_id.address, AccountAddress::ONE);
704 assert_eq!(module_id.name.as_str(), "coin");
705 assert_eq!(module_id.to_string(), "0x1::coin");
706 }
707
708 #[test]
709 fn test_module_id_invalid() {
710 assert!(MoveModuleId::from_str_strict("invalid").is_err());
711 assert!(MoveModuleId::from_str_strict("0x1").is_err());
712 assert!(MoveModuleId::from_str_strict("0x1::").is_err());
713 }
714
715 #[test]
716 fn test_struct_tag() {
717 let tag = StructTag::aptos_coin();
718 assert_eq!(tag.to_string(), "0x1::aptos_coin::AptosCoin");
719 }
720
721 #[test]
722 fn test_struct_tag_with_type_args() {
723 let coin_store = StructTag::new(
724 AccountAddress::ONE,
725 Identifier::new("coin").unwrap(),
726 Identifier::new("CoinStore").unwrap(),
727 vec![TypeTag::aptos_coin()],
728 );
729 assert!(coin_store.to_string().contains("CoinStore"));
730 assert!(coin_store.to_string().contains("AptosCoin"));
731 }
732
733 #[test]
734 fn test_struct_tag_aptos_coin() {
735 let tag = StructTag::aptos_coin();
736 assert_eq!(tag.address, AccountAddress::ONE);
737 assert_eq!(tag.module.as_str(), "aptos_coin");
738 assert_eq!(tag.name.as_str(), "AptosCoin");
739 }
740
741 #[test]
742 fn test_type_tag_display() {
743 assert_eq!(TypeTag::Bool.to_string(), "bool");
744 assert_eq!(TypeTag::U8.to_string(), "u8");
745 assert_eq!(TypeTag::U16.to_string(), "u16");
746 assert_eq!(TypeTag::U32.to_string(), "u32");
747 assert_eq!(TypeTag::U64.to_string(), "u64");
748 assert_eq!(TypeTag::U128.to_string(), "u128");
749 assert_eq!(TypeTag::U256.to_string(), "u256");
750 assert_eq!(TypeTag::Address.to_string(), "address");
751 assert_eq!(TypeTag::Signer.to_string(), "signer");
752 assert_eq!(TypeTag::vector(TypeTag::U8).to_string(), "vector<u8>");
753 assert_eq!(
754 TypeTag::aptos_coin().to_string(),
755 "0x1::aptos_coin::AptosCoin"
756 );
757 }
758
759 #[test]
760 fn test_type_tag_from_str_strict() {
761 assert_eq!(TypeTag::from_str_strict("bool").unwrap(), TypeTag::Bool);
762 assert_eq!(TypeTag::from_str_strict("u8").unwrap(), TypeTag::U8);
763 assert_eq!(TypeTag::from_str_strict("u64").unwrap(), TypeTag::U64);
764 assert_eq!(
765 TypeTag::from_str_strict("address").unwrap(),
766 TypeTag::Address
767 );
768 assert_eq!(TypeTag::from_str_strict("signer").unwrap(), TypeTag::Signer);
769 }
770
771 #[test]
772 fn test_type_tag_from_str_struct() {
773 let tag = TypeTag::from_str_strict("0x1::aptos_coin::AptosCoin").unwrap();
774 if let TypeTag::Struct(s) = tag {
775 assert_eq!(s.name.as_str(), "AptosCoin");
776 } else {
777 panic!("Expected struct type tag");
778 }
779 }
780
781 #[test]
782 fn test_type_tag_from_str_vector() {
783 let tag = TypeTag::from_str_strict("vector<u8>").unwrap();
784 if let TypeTag::Vector(inner) = tag {
785 assert_eq!(*inner, TypeTag::U8);
786 } else {
787 panic!("Expected vector type tag");
788 }
789 }
790
791 #[test]
792 fn test_type_tag_nested_vector() {
793 let tag = TypeTag::from_str_strict("vector<vector<u64>>").unwrap();
794 if let TypeTag::Vector(outer) = tag {
795 if let TypeTag::Vector(inner) = *outer {
796 assert_eq!(*inner, TypeTag::U64);
797 } else {
798 panic!("Expected nested vector");
799 }
800 } else {
801 panic!("Expected vector type tag");
802 }
803 }
804
805 #[test]
806 fn test_type_tag_invalid() {
807 assert!(TypeTag::from_str_strict("invalid").is_err());
808 assert!(TypeTag::from_str_strict("vector<").is_err());
809 }
810
811 #[test]
812 fn test_entry_function_id() {
813 let func = EntryFunctionId::from_str_strict("0x1::coin::transfer").unwrap();
814 assert_eq!(func.to_string(), "0x1::coin::transfer");
815 }
816
817 #[test]
818 fn test_entry_function_id_invalid() {
819 assert!(EntryFunctionId::from_str_strict("0x1::coin").is_err());
820 assert!(EntryFunctionId::from_str_strict("invalid").is_err());
821 }
822
823 #[test]
824 fn test_move_value_as_bool() {
825 let val = MoveValue::Bool(true);
826 assert_eq!(val.as_bool(), Some(true));
827 assert!(MoveValue::Number("123".to_string()).as_bool().is_none());
828 }
829
830 #[test]
831 fn test_move_value_as_u64() {
832 let val = MoveValue::Number("12345".to_string());
833 assert_eq!(val.as_u64(), Some(12345));
834 assert!(MoveValue::Bool(true).as_u64().is_none());
835 }
836
837 #[test]
838 fn test_move_value_as_u128() {
839 let val = MoveValue::Number("340282366920938463463374607431768211455".to_string());
840 assert_eq!(val.as_u128(), Some(u128::MAX));
841 }
842
843 #[test]
844 fn test_move_value_as_str() {
845 let val = MoveValue::String("hello".to_string());
846 assert_eq!(val.as_str(), Some("hello"));
847 let num = MoveValue::Number("123".to_string());
848 assert_eq!(num.as_str(), Some("123"));
849 }
850
851 #[test]
852 fn test_move_value_as_address() {
853 let val = MoveValue::Address(AccountAddress::ONE);
854 assert_eq!(val.as_address(), Some(&AccountAddress::ONE));
855 assert!(MoveValue::Bool(true).as_address().is_none());
856 }
857
858 #[test]
859 fn test_move_value_as_vec() {
860 let val = MoveValue::Vector(vec![MoveValue::Bool(true), MoveValue::Bool(false)]);
861 let vec = val.as_vec().unwrap();
862 assert_eq!(vec.len(), 2);
863 }
864
865 #[test]
866 fn test_move_resource_deserialization() {
867 let json = r#"{
868 "type": "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>",
869 "data": {"coin": {"value": "1000"}}
870 }"#;
871 let resource: MoveResource = serde_json::from_str(json).unwrap();
872 assert_eq!(
873 resource.typ,
874 "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"
875 );
876 }
877
878 #[test]
879 fn test_identifier_bcs_serialization() {
880 let id = Identifier::new("test_function").unwrap();
881 let serialized = aptos_bcs::to_bytes(&id).unwrap();
882 let deserialized: Identifier = aptos_bcs::from_bytes(&serialized).unwrap();
883 assert_eq!(id, deserialized);
884 }
885
886 #[test]
887 fn test_module_id_new() {
888 let module =
889 MoveModuleId::new(AccountAddress::ONE, Identifier::new("test_module").unwrap());
890 assert_eq!(module.address, AccountAddress::ONE);
891 assert_eq!(module.name.as_str(), "test_module");
892 }
893
894 #[test]
895 fn test_module_id_bcs_serialization() {
896 let module = MoveModuleId::from_str_strict("0x1::coin").unwrap();
897 let serialized = aptos_bcs::to_bytes(&module).unwrap();
898 let deserialized: MoveModuleId = aptos_bcs::from_bytes(&serialized).unwrap();
899 assert_eq!(module, deserialized);
900 }
901
902 #[test]
903 fn test_struct_tag_new() {
904 let tag = StructTag::new(
905 AccountAddress::from_hex("0x123").unwrap(),
906 Identifier::new("my_module").unwrap(),
907 Identifier::new("MyStruct").unwrap(),
908 vec![TypeTag::U64, TypeTag::Bool],
909 );
910 assert_eq!(tag.address, AccountAddress::from_hex("0x123").unwrap());
911 assert_eq!(tag.module.as_str(), "my_module");
912 assert_eq!(tag.name.as_str(), "MyStruct");
913 assert_eq!(tag.type_args.len(), 2);
914 }
915
916 #[test]
917 fn test_struct_tag_display_with_type_args() {
918 let tag = StructTag::new(
919 AccountAddress::ONE,
920 Identifier::new("coin").unwrap(),
921 Identifier::new("CoinStore").unwrap(),
922 vec![TypeTag::aptos_coin()],
923 );
924 let display = tag.to_string();
925 assert!(display.contains("CoinStore"));
926 assert!(display.contains('<'));
927 assert!(display.contains('>'));
928 }
929
930 #[test]
931 fn test_struct_tag_bcs_serialization() {
932 let tag = StructTag::aptos_coin();
933 let serialized = aptos_bcs::to_bytes(&tag).unwrap();
934 let deserialized: StructTag = aptos_bcs::from_bytes(&serialized).unwrap();
935 assert_eq!(tag, deserialized);
936 }
937
938 #[test]
939 fn test_type_tag_vector_constructor() {
940 let vec_type = TypeTag::vector(TypeTag::Address);
941 if let TypeTag::Vector(inner) = vec_type {
942 assert_eq!(*inner, TypeTag::Address);
943 } else {
944 panic!("Expected Vector type");
945 }
946 }
947
948 #[test]
949 fn test_type_tag_struct_constructor() {
950 let struct_type = TypeTag::struct_tag(StructTag::aptos_coin());
951 if let TypeTag::Struct(s) = struct_type {
952 assert_eq!(s.name.as_str(), "AptosCoin");
953 } else {
954 panic!("Expected Struct type");
955 }
956 }
957
958 #[test]
959 fn test_type_tag_from_str_u16_u32_u256() {
960 assert_eq!(TypeTag::from_str_strict("u16").unwrap(), TypeTag::U16);
961 assert_eq!(TypeTag::from_str_strict("u32").unwrap(), TypeTag::U32);
962 assert_eq!(TypeTag::from_str_strict("u256").unwrap(), TypeTag::U256);
963 }
964
965 #[test]
966 fn test_type_tag_from_str_vector_of_struct() {
967 let tag = TypeTag::from_str_strict("vector<0x1::aptos_coin::AptosCoin>").unwrap();
968 if let TypeTag::Vector(inner) = tag {
969 if let TypeTag::Struct(s) = *inner {
970 assert_eq!(s.name.as_str(), "AptosCoin");
971 } else {
972 panic!("Expected Struct inside Vector");
973 }
974 } else {
975 panic!("Expected Vector type");
976 }
977 }
978
979 #[test]
980 fn test_type_tag_from_str_struct_with_multiple_type_args() {
981 let tag = TypeTag::from_str_strict("0x1::table::Table<address, u64>").unwrap();
982 if let TypeTag::Struct(s) = tag {
983 assert_eq!(s.name.as_str(), "Table");
984 assert_eq!(s.type_args.len(), 2);
985 assert_eq!(s.type_args[0], TypeTag::Address);
986 assert_eq!(s.type_args[1], TypeTag::U64);
987 } else {
988 panic!("Expected Struct type");
989 }
990 }
991
992 #[test]
993 fn test_type_tag_from_str_malformed_generic() {
994 assert!(TypeTag::from_str_strict("vector<u8").is_err());
996 assert!(TypeTag::from_str_strict("vectoru8>").is_err());
998 assert!(TypeTag::from_str_strict("0x1::coin::Store<u64").is_err());
1000 }
1001
1002 #[test]
1003 fn test_type_tag_bcs_serialization() {
1004 let types = vec![
1005 TypeTag::Bool,
1006 TypeTag::U8,
1007 TypeTag::U16,
1008 TypeTag::U32,
1009 TypeTag::U64,
1010 TypeTag::U128,
1011 TypeTag::U256,
1012 TypeTag::Address,
1013 TypeTag::Signer,
1014 TypeTag::vector(TypeTag::U8),
1015 TypeTag::aptos_coin(),
1016 ];
1017
1018 for t in types {
1019 let serialized = aptos_bcs::to_bytes(&t).unwrap();
1020 let deserialized: TypeTag = aptos_bcs::from_bytes(&serialized).unwrap();
1021 assert_eq!(t, deserialized);
1022 }
1023 }
1024
1025 #[test]
1026 fn test_entry_function_id_new() {
1027 let module = MoveModuleId::from_str_strict("0x1::coin").unwrap();
1028 let name = Identifier::new("transfer").unwrap();
1029 let func = EntryFunctionId::new(module, name);
1030 assert_eq!(func.to_string(), "0x1::coin::transfer");
1031 }
1032
1033 #[test]
1034 fn test_entry_function_id_from_str() {
1035 let func: EntryFunctionId = "0x1::coin::transfer".parse().unwrap();
1036 assert_eq!(func.module.address, AccountAddress::ONE);
1037 assert_eq!(func.module.name.as_str(), "coin");
1038 assert_eq!(func.name.as_str(), "transfer");
1039 }
1040
1041 #[test]
1042 fn test_move_type_new_and_as_str() {
1043 let t = MoveType::new("0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>");
1044 assert_eq!(
1045 t.as_str(),
1046 "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"
1047 );
1048 }
1049
1050 #[test]
1051 fn test_move_type_display() {
1052 let t = MoveType::new("bool");
1053 assert_eq!(format!("{t}"), "bool");
1054 }
1055
1056 #[test]
1057 fn test_move_struct_fields() {
1058 let mut fields = serde_json::Map::new();
1059 fields.insert("value".to_string(), serde_json::json!("1000"));
1060 let s = MoveStruct { fields };
1061 assert_eq!(s.fields.get("value").unwrap(), &serde_json::json!("1000"));
1062 }
1063
1064 #[test]
1065 fn test_move_value_null() {
1066 let val = MoveValue::Null;
1067 assert!(val.as_bool().is_none());
1068 assert!(val.as_u64().is_none());
1069 assert!(val.as_str().is_none());
1070 assert!(val.as_address().is_none());
1071 assert!(val.as_vec().is_none());
1072 }
1073
1074 #[test]
1075 fn test_move_value_struct() {
1076 let mut fields = serde_json::Map::new();
1077 fields.insert("name".to_string(), serde_json::json!("test"));
1078 let s = MoveStruct { fields };
1079 let val = MoveValue::Struct(s);
1080
1081 if let MoveValue::Struct(inner) = val {
1083 assert!(inner.fields.contains_key("name"));
1084 } else {
1085 panic!("Expected Struct");
1086 }
1087 }
1088
1089 #[test]
1090 fn test_move_value_json_roundtrip() {
1091 let val = MoveValue::Bool(true);
1093 let json = serde_json::to_string(&val).unwrap();
1094 let deserialized: MoveValue = serde_json::from_str(&json).unwrap();
1095 assert_eq!(val, deserialized);
1096
1097 let val = MoveValue::Null;
1098 let json = serde_json::to_string(&val).unwrap();
1099 let deserialized: MoveValue = serde_json::from_str(&json).unwrap();
1100 assert_eq!(val, deserialized);
1101
1102 let val = MoveValue::Number("12345".to_string());
1105 let json = serde_json::to_string(&val).unwrap();
1106 assert_eq!(json, "\"12345\"");
1107
1108 let val = MoveValue::String("hello".to_string());
1109 let json = serde_json::to_string(&val).unwrap();
1110 assert_eq!(json, "\"hello\"");
1111 }
1112
1113 #[test]
1116 fn test_identifier_length_limit() {
1117 let valid = "a".repeat(128);
1119 assert!(Identifier::new(&valid).is_ok());
1120
1121 let too_long = "a".repeat(129);
1123 let result = Identifier::new(&too_long);
1124 assert!(result.is_err());
1125 assert!(result.unwrap_err().to_string().contains("too long"));
1126 }
1127
1128 #[test]
1129 fn test_type_tag_length_limit() {
1130 let valid = format!("0x1::{}::Test", "a".repeat(100));
1132 assert!(TypeTag::from_str_strict(&valid).is_ok());
1133
1134 let too_long = format!("0x1::{}::Test", "a".repeat(2000));
1136 let result = TypeTag::from_str_strict(&too_long);
1137 assert!(result.is_err());
1138 assert!(result.unwrap_err().to_string().contains("too long"));
1139 }
1140
1141 #[test]
1142 fn test_type_tag_nesting_depth_limit() {
1143 let valid = "vector<vector<vector<u8>>>";
1145 assert!(TypeTag::from_str_strict(valid).is_ok());
1146
1147 let too_deep = "vector<vector<vector<vector<vector<vector<vector<vector<vector<u8>>>>>>>>>";
1149 let result = TypeTag::from_str_strict(too_deep);
1150 assert!(result.is_err());
1151 assert!(result.unwrap_err().to_string().contains("too deep"));
1152 }
1153
1154 #[test]
1155 fn test_identifier_from_str() {
1156 let id: Identifier = "my_function".parse().unwrap();
1158 assert_eq!(id.as_str(), "my_function");
1159
1160 let result: Result<Identifier, _> = "123invalid".parse();
1162 assert!(result.is_err());
1163 }
1164
1165 #[test]
1166 fn test_module_id_from_str() {
1167 let module: MoveModuleId = "0x1::coin".parse().unwrap();
1169 assert_eq!(module.address, AccountAddress::ONE);
1170 assert_eq!(module.name.as_str(), "coin");
1171
1172 let result: Result<MoveModuleId, _> = "invalid".parse();
1174 assert!(result.is_err());
1175 }
1176
1177 #[test]
1178 fn test_struct_tag_simple() {
1179 let tag = StructTag::simple(AccountAddress::ONE, "coin", "CoinStore").unwrap();
1181 assert_eq!(tag.address, AccountAddress::ONE);
1182 assert_eq!(tag.module.as_str(), "coin");
1183 assert_eq!(tag.name.as_str(), "CoinStore");
1184 assert!(tag.type_args.is_empty());
1185
1186 let result = StructTag::simple(AccountAddress::ONE, "123invalid", "CoinStore");
1188 assert!(result.is_err());
1189
1190 let result = StructTag::simple(AccountAddress::ONE, "coin", "123invalid");
1192 assert!(result.is_err());
1193 }
1194
1195 #[test]
1196 fn test_struct_tag_display_with_multiple_type_args() {
1197 let tag = StructTag::new(
1199 AccountAddress::ONE,
1200 Identifier::new("table").unwrap(),
1201 Identifier::new("Table").unwrap(),
1202 vec![TypeTag::Address, TypeTag::U64, TypeTag::Bool],
1203 );
1204 let display = tag.to_string();
1205 assert_eq!(display, "0x1::table::Table<address, u64, bool>");
1206 }
1207
1208 #[test]
1209 fn test_type_tag_signed_integers_display() {
1210 assert_eq!(TypeTag::I8.to_string(), "i8");
1212 assert_eq!(TypeTag::I16.to_string(), "i16");
1213 assert_eq!(TypeTag::I32.to_string(), "i32");
1214 assert_eq!(TypeTag::I64.to_string(), "i64");
1215 assert_eq!(TypeTag::I128.to_string(), "i128");
1216 assert_eq!(TypeTag::I256.to_string(), "i256");
1217 }
1218
1219 #[test]
1220 fn test_move_value_as_u128_comprehensive() {
1221 let val = MoveValue::Number("340282366920938463463374607431768211455".to_string()); assert_eq!(val.as_u128(), Some(u128::MAX));
1224
1225 let val = MoveValue::Number("0".to_string());
1226 assert_eq!(val.as_u128(), Some(0));
1227
1228 let val = MoveValue::Bool(true);
1230 assert_eq!(val.as_u128(), None);
1231
1232 let val = MoveValue::Number("not_a_number".to_string());
1234 assert_eq!(val.as_u128(), None);
1235 }
1236
1237 #[test]
1238 fn test_type_tag_parse_empty_type_args() {
1239 let tag = TypeTag::from_str_strict("0x1::coin::CoinInfo").unwrap();
1241 if let TypeTag::Struct(s) = tag {
1242 assert!(s.type_args.is_empty());
1243 } else {
1244 panic!("Expected Struct");
1245 }
1246 }
1247
1248 #[test]
1249 fn test_type_tag_rejects_empty_type_args() {
1250 assert!(TypeTag::from_str_strict("0x1::a::B<>").is_err());
1252 assert!(TypeTag::from_str_strict("0x1::a::B<,>").is_err());
1254 assert!(TypeTag::from_str_strict("0x1::a::B<u8,,u16>").is_err());
1255 assert!(TypeTag::from_str_strict("0x1::a::B<,u8>").is_err());
1257 assert!(TypeTag::from_str_strict("0x1::a::B<u8,>").is_err());
1259 assert!(TypeTag::from_str_strict("vector<>").is_err());
1261 }
1262
1263 #[test]
1264 fn test_type_tag_whitespace_consistency() {
1265 let a = TypeTag::from_str_strict("0x1::a::B< u8 >").unwrap();
1267 let b = TypeTag::from_str_strict("0x1::a::B<u8>").unwrap();
1268 assert_eq!(a, b);
1269
1270 let v1 = TypeTag::from_str_strict("vector< u8 >").unwrap();
1271 let v2 = TypeTag::from_str_strict("vector<u8>").unwrap();
1272 assert_eq!(v1, v2);
1273 assert_eq!(v1, TypeTag::vector(TypeTag::U8));
1274
1275 let t = TypeTag::from_str_strict("0x1::table::Table< address , u64 >").unwrap();
1277 if let TypeTag::Struct(s) = t {
1278 assert_eq!(s.type_args, vec![TypeTag::Address, TypeTag::U64]);
1279 } else {
1280 panic!("expected struct");
1281 }
1282 }
1283
1284 #[test]
1285 fn test_type_tag_parse_nested_generics() {
1286 let tag = TypeTag::from_str_strict(
1288 "0x1::table::Table<0x1::string::String, vector<0x1::aptos_coin::AptosCoin>>",
1289 )
1290 .unwrap();
1291 if let TypeTag::Struct(s) = tag {
1292 assert_eq!(s.name.as_str(), "Table");
1293 assert_eq!(s.type_args.len(), 2);
1294 assert!(matches!(s.type_args[0], TypeTag::Struct(_)));
1296 assert!(matches!(s.type_args[1], TypeTag::Vector(_)));
1298 } else {
1299 panic!("Expected Struct");
1300 }
1301 }
1302}