iota_types/
type_input.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::fmt::{Display, Formatter};
6
7use anyhow::Result;
8use iota_macros::EnumVariantOrder;
9use move_core_types::{
10    account_address::AccountAddress,
11    identifier::Identifier,
12    language_storage::{StructTag, TypeTag},
13};
14use serde::{Deserialize, Serialize};
15
16#[derive(Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
17pub struct StructInput {
18    pub address: AccountAddress,
19    pub module: String,
20    pub name: String,
21    // alias for compatibility with old json serialized data.
22    #[serde(rename = "type_args", alias = "type_params")]
23    pub type_params: Vec<TypeInput>,
24}
25
26#[derive(
27    Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord, EnumVariantOrder,
28)]
29pub enum TypeInput {
30    // alias for compatibility with old json serialized data.
31    #[serde(rename = "bool", alias = "Bool")]
32    Bool,
33    #[serde(rename = "u8", alias = "U8")]
34    U8,
35    #[serde(rename = "u64", alias = "U64")]
36    U64,
37    #[serde(rename = "u128", alias = "U128")]
38    U128,
39    #[serde(rename = "address", alias = "Address")]
40    Address,
41    #[serde(rename = "signer", alias = "Signer")]
42    Signer,
43    #[serde(rename = "vector", alias = "Vector")]
44    Vector(Box<TypeInput>),
45    #[serde(rename = "struct", alias = "Struct")]
46    Struct(Box<StructInput>),
47
48    // NOTE: Added in bytecode version v6, do not reorder!
49    #[serde(rename = "u16", alias = "U16")]
50    U16,
51    #[serde(rename = "u32", alias = "U32")]
52    U32,
53    #[serde(rename = "u256", alias = "U256")]
54    U256,
55}
56
57impl TypeInput {
58    /// Return a canonical string representation of the type. All types are
59    /// represented using their source syntax:
60    ///
61    /// - "bool", "u8", "u16", "u32", "u64", "u128", "u256", "address",
62    ///   "signer", "vector" for ground types.
63    ///
64    /// - Structs are represented as fully qualified type names, with or without
65    ///   the prefix "0x" depending on the `with_prefix` flag, e.g.
66    ///   `0x000...0001::string::String` or
67    ///   `0x000...000a::m::T<0x000...000a::n::U<u64>>`.
68    ///
69    /// - Addresses are hex-encoded lowercase values of length 32 (zero-padded).
70    ///
71    /// Note: this function is guaranteed to be stable -- suitable for use
72    /// inside Move native functions or the VM. By contrast, this type's
73    /// `Display` implementation is subject to change and should be used
74    /// inside code that needs to return a stable output (e.g. that might be
75    /// committed to effects on-chain).
76    pub fn to_canonical_string(&self, with_prefix: bool) -> String {
77        self.to_canonical_display(with_prefix).to_string()
78    }
79
80    /// Return the canonical string representation of the TypeTag conditionally
81    /// with prefix 0x
82    pub fn to_canonical_display(&self, with_prefix: bool) -> impl std::fmt::Display + '_ {
83        struct CanonicalDisplay<'a> {
84            data: &'a TypeInput,
85            with_prefix: bool,
86        }
87
88        impl std::fmt::Display for CanonicalDisplay<'_> {
89            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
90                match self.data {
91                    TypeInput::Bool => write!(f, "bool"),
92                    TypeInput::U8 => write!(f, "u8"),
93                    TypeInput::U16 => write!(f, "u16"),
94                    TypeInput::U32 => write!(f, "u32"),
95                    TypeInput::U64 => write!(f, "u64"),
96                    TypeInput::U128 => write!(f, "u128"),
97                    TypeInput::U256 => write!(f, "u256"),
98                    TypeInput::Address => write!(f, "address"),
99                    TypeInput::Signer => write!(f, "signer"),
100                    TypeInput::Vector(t) => {
101                        write!(f, "vector<{}>", t.to_canonical_display(self.with_prefix))
102                    }
103                    TypeInput::Struct(s) => {
104                        write!(f, "{}", s.to_canonical_display(self.with_prefix))
105                    }
106                }
107            }
108        }
109
110        CanonicalDisplay {
111            data: self,
112            with_prefix,
113        }
114    }
115
116    /// Convert the TypeInput into a TypeTag without checking for validity of
117    /// identifiers within the StructTag. DO NOT USE UNLESS YOU KNOW WHAT
118    /// YOU ARE DOING AND WHY THIS IS SAFE TO CALL.
119    ///
120    /// # Safety
121    ///
122    /// Preserving existing behaviour for identifier deserialization within type
123    /// tags and inputs.
124    pub unsafe fn into_type_tag_unchecked(self) -> TypeTag {
125        match self {
126            TypeInput::Bool => TypeTag::Bool,
127            TypeInput::U8 => TypeTag::U8,
128            TypeInput::U16 => TypeTag::U16,
129            TypeInput::U32 => TypeTag::U32,
130            TypeInput::U64 => TypeTag::U64,
131            TypeInput::U128 => TypeTag::U128,
132            TypeInput::U256 => TypeTag::U256,
133            TypeInput::Address => TypeTag::Address,
134            TypeInput::Signer => TypeTag::Signer,
135            TypeInput::Vector(inner) => TypeTag::Vector(Box::new(inner.into_type_tag_unchecked())),
136            TypeInput::Struct(inner) => {
137                let StructInput {
138                    address,
139                    module,
140                    name,
141                    type_params,
142                } = *inner;
143                TypeTag::Struct(Box::new(StructTag {
144                    address,
145                    module: Identifier::new_unchecked(module),
146                    name: Identifier::new_unchecked(name),
147                    type_params: type_params
148                        .into_iter()
149                        .map(|ty| ty.into_type_tag_unchecked())
150                        .collect(),
151                }))
152            }
153        }
154    }
155
156    /// Convert to a `TypeTag` consuming `self`. This can fail if this value
157    /// includes invalid identifiers.
158    pub fn into_type_tag(self) -> Result<TypeTag> {
159        self.as_type_tag()
160    }
161
162    /// Conversion to a `TypeTag`, which can fail if this value includes invalid
163    /// identifiers.
164    pub fn as_type_tag(&self) -> Result<TypeTag> {
165        // Keep this in sync with implementation in:
166        // crates/iota-package-resolver/src/lib.rs
167        use TypeInput as I;
168        use TypeTag as T;
169        Ok(match self {
170            I::Bool => T::Bool,
171            I::U8 => T::U8,
172            I::U16 => T::U16,
173            I::U32 => T::U32,
174            I::U64 => T::U64,
175            I::U128 => T::U128,
176            I::U256 => T::U256,
177            I::Address => T::Address,
178            I::Signer => T::Signer,
179            I::Vector(t) => T::Vector(Box::new(t.as_type_tag()?)),
180            I::Struct(s) => {
181                let StructInput {
182                    address,
183                    module,
184                    name,
185                    type_params,
186                } = s.as_ref();
187                let type_params = type_params
188                    .iter()
189                    .map(|t| t.as_type_tag())
190                    .collect::<Result<_>>()?;
191                T::Struct(Box::new(StructTag {
192                    address: *address,
193                    module: Identifier::new(module.to_owned())?,
194                    name: Identifier::new(name.to_owned())?,
195                    type_params,
196                }))
197            }
198        })
199    }
200}
201
202impl StructInput {
203    /// Return a canonical string representation of the struct.
204    ///
205    /// - Structs are represented as fully qualified type names, with or without
206    ///   the prefix "0x" depending on the `with_prefix` flag, e.g.
207    ///   `0x000...0001::string::String` or
208    ///   `0x000...000a::m::T<0x000...000a::n::U<u64>>`.
209    ///
210    /// - Addresses are hex-encoded lowercase values of length 32 (zero-padded).
211    ///
212    /// Note: this function is guaranteed to be stable -- suitable for use
213    /// inside Move native functions or the VM. By contrast, this type's
214    /// `Display` implementation is subject to change and should be used
215    /// inside code that needs to return a stable output (e.g. that might be
216    /// committed to effects on-chain).
217    pub fn to_canonical_string(&self, with_prefix: bool) -> String {
218        self.to_canonical_display(with_prefix).to_string()
219    }
220
221    /// Implements the canonical string representation of the StructTag with the
222    /// prefix 0x
223    pub fn to_canonical_display(&self, with_prefix: bool) -> impl std::fmt::Display + '_ {
224        struct CanonicalDisplay<'a> {
225            data: &'a StructInput,
226            with_prefix: bool,
227        }
228
229        impl std::fmt::Display for CanonicalDisplay<'_> {
230            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231                write!(
232                    f,
233                    "{}::{}::{}",
234                    self.data.address.to_canonical_display(self.with_prefix),
235                    self.data.module,
236                    self.data.name
237                )?;
238
239                if let Some(first_ty) = self.data.type_params.first() {
240                    write!(f, "<")?;
241                    write!(f, "{}", first_ty.to_canonical_display(self.with_prefix))?;
242                    for ty in self.data.type_params.iter().skip(1) {
243                        // Note that unlike Display for StructTag, there is no space between the
244                        // comma and canonical display. This follows the
245                        // original to_canonical_string() implementation.
246                        write!(f, ",{}", ty.to_canonical_display(self.with_prefix))?;
247                    }
248                    write!(f, ">")?;
249                }
250                Ok(())
251            }
252        }
253
254        CanonicalDisplay {
255            data: self,
256            with_prefix,
257        }
258    }
259}
260
261impl From<TypeTag> for TypeInput {
262    fn from(tag: TypeTag) -> Self {
263        match tag {
264            TypeTag::Bool => TypeInput::Bool,
265            TypeTag::U8 => TypeInput::U8,
266            TypeTag::U64 => TypeInput::U64,
267            TypeTag::U128 => TypeInput::U128,
268            TypeTag::Address => TypeInput::Address,
269            TypeTag::Signer => TypeInput::Signer,
270            TypeTag::Vector(inner) => TypeInput::Vector(Box::new(TypeInput::from(*inner))),
271            TypeTag::Struct(inner) => TypeInput::Struct(Box::new(StructInput::from(*inner))),
272            TypeTag::U16 => TypeInput::U16,
273            TypeTag::U32 => TypeInput::U32,
274            TypeTag::U256 => TypeInput::U256,
275        }
276    }
277}
278
279impl From<StructTag> for StructInput {
280    fn from(tag: StructTag) -> Self {
281        StructInput {
282            address: tag.address,
283            module: tag.module.to_string(),
284            name: tag.name.to_string(),
285            type_params: tag.type_params.into_iter().map(TypeInput::from).collect(),
286        }
287    }
288}
289
290impl From<StructInput> for TypeInput {
291    fn from(t: StructInput) -> TypeInput {
292        TypeInput::Struct(Box::new(t))
293    }
294}
295
296impl Display for StructInput {
297    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
298        write!(
299            f,
300            "0x{}::{}::{}",
301            self.address.short_str_lossless(),
302            self.module,
303            self.name
304        )?;
305
306        let mut prefix = "<";
307        for ty in &self.type_params {
308            write!(f, "{}{}", prefix, ty)?;
309            prefix = ", ";
310        }
311        if !self.type_params.is_empty() {
312            write!(f, ">")?;
313        }
314
315        Ok(())
316    }
317}
318
319impl Display for TypeInput {
320    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
321        match self {
322            TypeInput::Struct(s) => write!(f, "{}", s),
323            TypeInput::Vector(ty) => write!(f, "vector<{}>", ty),
324            TypeInput::U8 => write!(f, "u8"),
325            TypeInput::U16 => write!(f, "u16"),
326            TypeInput::U32 => write!(f, "u32"),
327            TypeInput::U64 => write!(f, "u64"),
328            TypeInput::U128 => write!(f, "u128"),
329            TypeInput::U256 => write!(f, "u256"),
330            TypeInput::Address => write!(f, "address"),
331            TypeInput::Signer => write!(f, "signer"),
332            TypeInput::Bool => write!(f, "bool"),
333        }
334    }
335}
336
337#[cfg(test)]
338mod test {
339    use iota_enum_compat_util::check_enum_compat_order;
340
341    use super::TypeInput;
342
343    #[test]
344    fn enforce_order_test() {
345        let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
346        path.extend(["tests", "staged", "type_input.yaml"]);
347        check_enum_compat_order::<TypeInput>(path);
348    }
349}