Skip to main content

af_move_type/
any.rs

1//! Permissive type marker that accepts any Move type in its slot.
2
3use std::str::FromStr;
4
5use af_sui_types::TypeTag;
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7
8use crate::{MoveType, MoveTypeTag, ParseTypeTagError, TypeTagError};
9
10/// Generic type that accepts **any** Move type argument in its slot,
11/// including phantom-paramed generics such as `VENDOR<X>` as well as
12/// non-struct types (primitives, vectors).
13///
14/// Where [`crate::otw::Otw`] requires `n_types_expected == 0` and therefore
15/// rejects phantom-paramed arguments, `AnyT` performs no validation — its
16/// associated [`AnyTTypeTag`] simply captures the raw [`TypeTag`] so it
17/// can be read back unchanged.
18///
19/// `AnyT` is intended for **phantom slots only** in generic Move types
20/// (e.g. `AuthorityCap<AnyT, AnyT>`). It is never instantiated as a real
21/// value; its [`MoveType`] impl exists so the derive-generated parsers can
22/// thread phantom-paramed type arguments through unchanged.
23///
24/// Note: `AnyT` deliberately does **not** implement [`MoveStruct`] —
25/// `MoveStructTag: TryFrom<StructTag>` is incompatible with carrying an
26/// arbitrary [`TypeTag`] (which may be non-struct). Use [`crate::otw::Otw`]
27/// when you need a `MoveStruct`-bounded slot type.
28///
29/// Unlike `Otw`, `AnyT` does **not** implement [`crate::StaticTypeTag`] or
30/// its `Static*` siblings: it has no statically known type tag, by design.
31/// Use it on the runtime decode path (e.g.
32/// [`MoveInstance::from_raw_type`](crate::MoveInstance::from_raw_type)).
33///
34/// [`MoveStruct`]: crate::MoveStruct
35#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq, Hash)]
36pub struct AnyT {
37    dummy_field: bool,
38}
39
40impl AnyT {
41    pub fn new() -> Self {
42        Self::default()
43    }
44}
45
46impl std::fmt::Display for AnyT {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        write!(f, "AnyT")
49    }
50}
51
52/// `TypeTag` companion for [`AnyT`]. Wraps the raw [`TypeTag`] from the slot
53/// with **no** validation of variant, address, module, name, or type-param
54/// count.
55#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
56pub struct AnyTTypeTag(pub TypeTag);
57
58impl From<AnyTTypeTag> for TypeTag {
59    fn from(value: AnyTTypeTag) -> Self {
60        value.0
61    }
62}
63
64impl TryFrom<TypeTag> for AnyTTypeTag {
65    type Error = TypeTagError;
66
67    fn try_from(value: TypeTag) -> Result<Self, Self::Error> {
68        Ok(Self(value))
69    }
70}
71
72impl MoveTypeTag for AnyTTypeTag {
73    fn matches(_tag: &TypeTag) -> bool {
74        true
75    }
76
77    fn matches_instance(&self, _tag: &TypeTag) -> bool {
78        true
79    }
80}
81
82impl FromStr for AnyTTypeTag {
83    type Err = ParseTypeTagError;
84
85    fn from_str(s: &str) -> Result<Self, Self::Err> {
86        let tag: TypeTag = s.parse()?;
87        Ok(Self(tag))
88    }
89}
90
91impl std::fmt::Display for AnyTTypeTag {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        write!(f, "{}", self.0)
94    }
95}
96
97impl Serialize for AnyTTypeTag {
98    fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
99        ser.collect_str(&self.0)
100    }
101}
102
103impl<'de> Deserialize<'de> for AnyTTypeTag {
104    fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
105        use serde::de::Error as _;
106        let s = String::deserialize(de)?;
107        s.parse().map_err(D::Error::custom)
108    }
109}
110
111impl MoveType for AnyT {
112    type TypeTag = AnyTTypeTag;
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    /// `AnyTTypeTag` accepts a struct tag whose own type params carry
120    /// phantom-paramed structs — the exact case `Otw` rejects.
121    #[test]
122    fn accepts_phantom_paramed_struct_tag() {
123        let raw: TypeTag = "0x1::authority::AuthorityCap<0x1::authority::VENDOR<0x2::aftermath::AFTERMATH>, 0x1::authority::ASSISTANT>"
124            .parse()
125            .unwrap();
126        let wrapped = AnyTTypeTag::try_from(raw.clone()).unwrap();
127        let back: TypeTag = wrapped.into();
128        assert_eq!(back, raw);
129    }
130
131    /// `AnyTTypeTag` is permissive over `TypeTag` variants — not just
132    /// `Struct(_)` — unlike a `MoveStruct`'s derived companion.
133    #[test]
134    fn accepts_non_struct_type_tag() {
135        for raw in [TypeTag::U64, TypeTag::Bool, TypeTag::Address] {
136            let wrapped = AnyTTypeTag::try_from(raw.clone()).unwrap();
137            assert_eq!(TypeTag::from(wrapped), raw);
138        }
139    }
140
141    /// `Display` / `FromStr` round-trip through the canonical TypeTag form.
142    #[test]
143    fn display_fromstr_roundtrip() {
144        let raw: TypeTag = "0x1::a::B<0x2::c::D<0x3::e::F>>".parse().unwrap();
145        let wrapped = AnyTTypeTag(raw.clone());
146        let s = wrapped.to_string();
147        let parsed = AnyTTypeTag::from_str(&s).unwrap();
148        assert_eq!(parsed.0, raw);
149    }
150
151    /// `Serialize` / `Deserialize` round-trip via JSON.
152    #[test]
153    fn serde_roundtrip_json() {
154        let raw: TypeTag = "0x1::a::B<0x2::c::D<0x3::e::F>>".parse().unwrap();
155        let wrapped = AnyTTypeTag(raw.clone());
156        let json = serde_json::to_string(&wrapped).unwrap();
157        let back: AnyTTypeTag = serde_json::from_str(&json).unwrap();
158        assert_eq!(back.0, raw);
159    }
160}