1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Wrapper type for serializing Move struct tags as strings.

use anyhow::Result;
use move_core_types::{
    account_address::AccountAddress, language_storage::StructTag, parser::parse_struct_tag,
};
use schemars::{
    schema::{InstanceType, SchemaObject, StringValidation},
    JsonSchema,
};
use serde::{Deserialize, Serialize, Serializer};
use std::{fmt::Display, ops::Deref, str::FromStr};

/// Wrapper around [StructTag] which is serialized as a string.
#[derive(Debug, PartialEq, Hash, Eq, Clone, PartialOrd, Ord)]
pub struct StructTagData(StructTag);

impl JsonSchema for StructTagData {
    fn schema_name() -> String {
        "StructTag".to_string()
    }

    fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
        SchemaObject {
            instance_type: Some(InstanceType::String.into()),
            string: Some(Box::new(StringValidation {
                pattern: Some(format!(
                    "^0x[a-fA-F0-9]{{1,{}}}::[\\w]+::[\\w]+$",
                    AccountAddress::LENGTH
                )),
                ..Default::default()
            })),
            ..Default::default()
        }
        .into()
    }
}

impl Deref for StructTagData {
    type Target = StructTag;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Display for StructTagData {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

impl Serialize for StructTagData {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.to_string().as_str())
    }
}

impl From<&StructTag> for StructTagData {
    fn from(id: &StructTag) -> Self {
        Self(id.clone())
    }
}

impl From<StructTag> for StructTagData {
    fn from(id: StructTag) -> Self {
        Self(id)
    }
}

impl From<&StructTagData> for StructTag {
    fn from(id: &StructTagData) -> Self {
        id.0.clone()
    }
}

impl From<StructTagData> for StructTag {
    fn from(id: StructTagData) -> Self {
        id.0
    }
}

impl<'de> Deserialize<'de> for StructTagData {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        Ok(parse_struct_tag(&s)
            .map_err(serde::de::Error::custom)?
            .into())
    }
}

impl FromStr for StructTagData {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(parse_struct_tag(&format!("{}::Dummy", s))?.into())
    }
}

/// Parses a module ID.
///
/// # Example
///
/// ```
/// use move_core_types::language_storage::StructTag;
/// let id: StructTag = module_id::parse_module_id("0x1::Errors").unwrap().into();
/// assert_eq!("0x1::Errors", id.short_str_lossless());
/// ```
pub fn parse_module_id(raw: &str) -> Result<StructTagData> {
    StructTagData::from_str(raw)
}

#[cfg(test)]
mod tests {
    use crate::StructTagData;
    use move_core_types::parser::parse_struct_tag;

    #[test]
    fn test_serde() {
        let my_struct_tag: StructTagData = parse_struct_tag("0x1::A::B").unwrap().into();

        let ser = serde_json::to_string(&my_struct_tag).unwrap();
        let des: StructTagData = serde_json::from_str(&ser).unwrap();

        assert_eq!(my_struct_tag, des);
        assert_eq!(my_struct_tag.to_string(), "0x1::A::B");
    }
}