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