1use std::fmt::{Display, Formatter};
5
6use anyhow::Result;
7use move_core_types::{
8    account_address::AccountAddress,
9    identifier::Identifier,
10    language_storage::{StructTag, TypeTag},
11};
12use serde::{Deserialize, Serialize};
13use sui_macros::EnumVariantOrder;
14
15use crate::base_types::MoveObjectType;
16
17#[derive(Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
18pub struct StructInput {
19    pub address: AccountAddress,
20    pub module: String,
21    pub name: String,
22    #[serde(rename = "type_args", alias = "type_params")]
24    pub type_params: Vec<TypeInput>,
25}
26
27#[derive(
28    Serialize, Deserialize, Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord, EnumVariantOrder,
29)]
30pub enum TypeInput {
31    #[serde(rename = "bool", alias = "Bool")]
33    Bool,
34    #[serde(rename = "u8", alias = "U8")]
35    U8,
36    #[serde(rename = "u64", alias = "U64")]
37    U64,
38    #[serde(rename = "u128", alias = "U128")]
39    U128,
40    #[serde(rename = "address", alias = "Address")]
41    Address,
42    #[serde(rename = "signer", alias = "Signer")]
43    Signer,
44    #[serde(rename = "vector", alias = "Vector")]
45    Vector(Box<TypeInput>),
46    #[serde(rename = "struct", alias = "Struct")]
47    Struct(Box<StructInput>),
48
49    #[serde(rename = "u16", alias = "U16")]
51    U16,
52    #[serde(rename = "u32", alias = "U32")]
53    U32,
54    #[serde(rename = "u256", alias = "U256")]
55    U256,
56}
57
58impl TypeInput {
59    pub fn to_canonical_string(&self, with_prefix: bool) -> String {
76        self.to_canonical_display(with_prefix).to_string()
77    }
78
79    pub fn to_canonical_display(&self, with_prefix: bool) -> impl std::fmt::Display + '_ {
81        struct CanonicalDisplay<'a> {
82            data: &'a TypeInput,
83            with_prefix: bool,
84        }
85
86        impl std::fmt::Display for CanonicalDisplay<'_> {
87            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
88                match self.data {
89                    TypeInput::Bool => write!(f, "bool"),
90                    TypeInput::U8 => write!(f, "u8"),
91                    TypeInput::U16 => write!(f, "u16"),
92                    TypeInput::U32 => write!(f, "u32"),
93                    TypeInput::U64 => write!(f, "u64"),
94                    TypeInput::U128 => write!(f, "u128"),
95                    TypeInput::U256 => write!(f, "u256"),
96                    TypeInput::Address => write!(f, "address"),
97                    TypeInput::Signer => write!(f, "signer"),
98                    TypeInput::Vector(t) => {
99                        write!(f, "vector<{}>", t.to_canonical_display(self.with_prefix))
100                    }
101                    TypeInput::Struct(s) => {
102                        write!(f, "{}", s.to_canonical_display(self.with_prefix))
103                    }
104                }
105            }
106        }
107
108        CanonicalDisplay {
109            data: self,
110            with_prefix,
111        }
112    }
113
114    pub unsafe fn into_type_tag_unchecked(self) -> TypeTag {
122        match self {
123            TypeInput::Bool => TypeTag::Bool,
124            TypeInput::U8 => TypeTag::U8,
125            TypeInput::U16 => TypeTag::U16,
126            TypeInput::U32 => TypeTag::U32,
127            TypeInput::U64 => TypeTag::U64,
128            TypeInput::U128 => TypeTag::U128,
129            TypeInput::U256 => TypeTag::U256,
130            TypeInput::Address => TypeTag::Address,
131            TypeInput::Signer => TypeTag::Signer,
132            TypeInput::Vector(inner) => unsafe {
133                TypeTag::Vector(Box::new(inner.into_type_tag_unchecked()))
134            },
135            TypeInput::Struct(inner) => {
136                let StructInput {
137                    address,
138                    module,
139                    name,
140                    type_params,
141                } = *inner;
142                unsafe {
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
157    pub fn to_type_tag(&self) -> Result<TypeTag> {
161        use TypeInput as I;
162        use TypeTag as T;
163        Ok(match self {
164            I::Bool => T::Bool,
165            I::U8 => T::U8,
166            I::U16 => T::U16,
167            I::U32 => T::U32,
168            I::U64 => T::U64,
169            I::U128 => T::U128,
170            I::U256 => T::U256,
171            I::Address => T::Address,
172            I::Signer => T::Signer,
173            I::Vector(t) => T::Vector(Box::new(t.to_type_tag()?)),
174            I::Struct(s) => {
175                let StructInput {
176                    address,
177                    module,
178                    name,
179                    type_params,
180                } = s.as_ref();
181                let type_params = type_params
182                    .iter()
183                    .map(|t| t.to_type_tag())
184                    .collect::<Result<_>>()?;
185                T::Struct(Box::new(StructTag {
186                    address: *address,
187                    module: Identifier::new(module.to_owned())?,
188                    name: Identifier::new(name.to_owned())?,
189                    type_params,
190                }))
191            }
192        })
193    }
194}
195
196impl StructInput {
197    pub fn to_canonical_string(&self, with_prefix: bool) -> String {
210        self.to_canonical_display(with_prefix).to_string()
211    }
212
213    pub fn to_canonical_display(&self, with_prefix: bool) -> impl std::fmt::Display + '_ {
215        struct CanonicalDisplay<'a> {
216            data: &'a StructInput,
217            with_prefix: bool,
218        }
219
220        impl std::fmt::Display for CanonicalDisplay<'_> {
221            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222                write!(
223                    f,
224                    "{}::{}::{}",
225                    self.data.address.to_canonical_display(self.with_prefix),
226                    self.data.module,
227                    self.data.name
228                )?;
229
230                if let Some(first_ty) = self.data.type_params.first() {
231                    write!(f, "<")?;
232                    write!(f, "{}", first_ty.to_canonical_display(self.with_prefix))?;
233                    for ty in self.data.type_params.iter().skip(1) {
234                        write!(f, ",{}", ty.to_canonical_display(self.with_prefix))?;
237                    }
238                    write!(f, ">")?;
239                }
240                Ok(())
241            }
242        }
243
244        CanonicalDisplay {
245            data: self,
246            with_prefix,
247        }
248    }
249}
250
251impl From<MoveObjectType> for TypeInput {
252    fn from(obj: MoveObjectType) -> Self {
253        Self::from(TypeTag::from(obj))
254    }
255}
256
257impl From<TypeTag> for TypeInput {
258    fn from(tag: TypeTag) -> Self {
259        match tag {
260            TypeTag::Bool => TypeInput::Bool,
261            TypeTag::U8 => TypeInput::U8,
262            TypeTag::U64 => TypeInput::U64,
263            TypeTag::U128 => TypeInput::U128,
264            TypeTag::Address => TypeInput::Address,
265            TypeTag::Signer => TypeInput::Signer,
266            TypeTag::Vector(inner) => TypeInput::Vector(Box::new(TypeInput::from(*inner))),
267            TypeTag::Struct(inner) => TypeInput::Struct(Box::new(StructInput::from(*inner))),
268            TypeTag::U16 => TypeInput::U16,
269            TypeTag::U32 => TypeInput::U32,
270            TypeTag::U256 => TypeInput::U256,
271        }
272    }
273}
274
275impl From<StructTag> for StructInput {
276    fn from(tag: StructTag) -> Self {
277        StructInput {
278            address: tag.address,
279            module: tag.module.to_string(),
280            name: tag.name.to_string(),
281            type_params: tag.type_params.into_iter().map(TypeInput::from).collect(),
282        }
283    }
284}
285
286impl From<StructInput> for TypeInput {
287    fn from(t: StructInput) -> TypeInput {
288        TypeInput::Struct(Box::new(t))
289    }
290}
291
292impl Display for StructInput {
293    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
294        write!(
295            f,
296            "0x{}::{}::{}",
297            self.address.short_str_lossless(),
298            self.module,
299            self.name
300        )?;
301
302        let mut prefix = "<";
303        for ty in &self.type_params {
304            write!(f, "{}{}", prefix, ty)?;
305            prefix = ", ";
306        }
307        if !self.type_params.is_empty() {
308            write!(f, ">")?;
309        }
310
311        Ok(())
312    }
313}
314
315impl Display for TypeInput {
316    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
317        match self {
318            TypeInput::Struct(s) => write!(f, "{}", s),
319            TypeInput::Vector(ty) => write!(f, "vector<{}>", ty),
320            TypeInput::U8 => write!(f, "u8"),
321            TypeInput::U16 => write!(f, "u16"),
322            TypeInput::U32 => write!(f, "u32"),
323            TypeInput::U64 => write!(f, "u64"),
324            TypeInput::U128 => write!(f, "u128"),
325            TypeInput::U256 => write!(f, "u256"),
326            TypeInput::Address => write!(f, "address"),
327            TypeInput::Signer => write!(f, "signer"),
328            TypeInput::Bool => write!(f, "bool"),
329        }
330    }
331}
332
333#[cfg(test)]
334mod test {
335    use super::TypeInput;
336    use sui_enum_compat_util::*;
337
338    #[test]
339    fn enforce_order_test() {
340        let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
341        path.extend(["tests", "staged", "type_input.yaml"]);
342        check_enum_compat_order::<TypeInput>(path);
343    }
344}