alloy_json_abi/
internal_type.rs

1use alloc::string::{String, ToString};
2use core::fmt;
3use parser::TypeSpecifier;
4use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
5
6/// The contract internal type. This could be a regular Solidity type, a
7/// user-defined type, an enum, a struct, a contract, or an address payable.
8///
9/// The internal type represents the Solidity definition of the type, stripped
10/// of the memory or storage keywords. It is used to convey the application dev
11/// and user-facing type, while the json param "type" field is used to convey
12/// the underlying ABI type.
13#[derive(Clone, Debug, PartialEq, Eq, Hash)]
14pub enum InternalType {
15    /// Address payable.
16    AddressPayable(String),
17    /// Contract.
18    Contract(String),
19    /// Enum. Possibly of the form `contract.enum`.
20    Enum {
21        /// Contract qualifier, if any
22        contract: Option<String>,
23        /// Enum name
24        ty: String,
25    },
26    /// Struct. Possibly of the form `contract.struct`.
27    Struct {
28        /// Contract qualifier, if any
29        contract: Option<String>,
30        /// Struct name
31        ty: String,
32    },
33    /// Other. Possible of the form `contract.other`.
34    Other {
35        /// Contract qualifier, if any
36        contract: Option<String>,
37        /// Struct name
38        ty: String,
39    },
40}
41
42impl fmt::Display for InternalType {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        self.as_borrowed().fmt(f)
45    }
46}
47
48impl Serialize for InternalType {
49    #[inline]
50    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
51        self.as_borrowed().serialize(serializer)
52    }
53}
54
55impl<'de> Deserialize<'de> for InternalType {
56    #[inline]
57    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
58        deserializer.deserialize_str(ItVisitor)
59    }
60}
61
62impl InternalType {
63    /// Parse a string into an instance, taking ownership of data
64    #[inline]
65    pub fn parse(s: &str) -> Option<Self> {
66        BorrowedInternalType::parse(s).map(BorrowedInternalType::into_owned)
67    }
68
69    /// True if the instance is a `struct` variant.
70    #[inline]
71    pub const fn is_struct(&self) -> bool {
72        matches!(self, Self::Struct { .. })
73    }
74
75    /// True if the instance is a `enum` variant.
76    #[inline]
77    pub const fn is_enum(&self) -> bool {
78        matches!(self, Self::Enum { .. })
79    }
80
81    /// True if the instance is a `contract` variant.
82    #[inline]
83    pub const fn is_contract(&self) -> bool {
84        matches!(self, Self::Contract(_))
85    }
86
87    /// True if the instance is a `address payable` variant.
88    #[inline]
89    pub const fn is_address_payable(&self) -> bool {
90        matches!(self, Self::AddressPayable(_))
91    }
92
93    /// True if the instance is a `other` variant.
94    #[inline]
95    pub const fn is_other(&self) -> bool {
96        matches!(self, Self::Other { .. })
97    }
98
99    /// Fallible conversion to a variant.
100    #[inline]
101    pub fn as_struct(&self) -> Option<(Option<&str>, &str)> {
102        match self {
103            Self::Struct { contract, ty } => Some((contract.as_deref(), ty)),
104            _ => None,
105        }
106    }
107
108    /// Fallible conversion to a variant.
109    #[inline]
110    pub fn as_enum(&self) -> Option<(Option<&str>, &str)> {
111        match self {
112            Self::Enum { contract, ty } => Some((contract.as_deref(), ty)),
113            _ => None,
114        }
115    }
116
117    /// Fallible conversion to a variant.
118    #[inline]
119    pub fn as_contract(&self) -> Option<&str> {
120        match self {
121            Self::Contract(s) => Some(s),
122            _ => None,
123        }
124    }
125
126    /// Fallible conversion to a variant.
127    #[inline]
128    pub fn as_other(&self) -> Option<(Option<&str>, &str)> {
129        match self {
130            Self::Other { contract, ty } => Some((contract.as_deref(), ty)),
131            _ => None,
132        }
133    }
134
135    /// Return a [`TypeSpecifier`] describing the struct if this type is a
136    /// struct.
137    #[inline]
138    pub fn struct_specifier(&self) -> Option<TypeSpecifier<'_>> {
139        self.as_struct().and_then(|s| TypeSpecifier::parse(s.1).ok())
140    }
141
142    /// Return a [`TypeSpecifier`] describing the enum if this type is an enum.
143    #[inline]
144    pub fn enum_specifier(&self) -> Option<TypeSpecifier<'_>> {
145        self.as_enum().and_then(|s| TypeSpecifier::parse(s.1).ok())
146    }
147
148    /// Return a [`TypeSpecifier`] describing the contract if this type is a
149    /// contract.
150    #[inline]
151    pub fn contract_specifier(&self) -> Option<TypeSpecifier<'_>> {
152        self.as_contract().and_then(|s| TypeSpecifier::parse(s).ok())
153    }
154
155    /// Return a [`TypeSpecifier`] describing the other if this type is an
156    /// other. An "other" specifier indicates EITHER a regular Solidity type OR
157    /// a user-defined type. It is not possible to distinguish between the two
158    /// without additional context.
159    #[inline]
160    pub fn other_specifier(&self) -> Option<TypeSpecifier<'_>> {
161        self.as_other().and_then(|s| TypeSpecifier::parse(s.1).ok())
162    }
163
164    #[inline]
165    pub(crate) fn as_borrowed(&self) -> BorrowedInternalType<'_> {
166        match self {
167            Self::AddressPayable(s) => BorrowedInternalType::AddressPayable(s),
168            Self::Contract(s) => BorrowedInternalType::Contract(s),
169            Self::Enum { contract, ty } => {
170                BorrowedInternalType::Enum { contract: contract.as_deref(), ty }
171            }
172            Self::Struct { contract, ty } => {
173                BorrowedInternalType::Struct { contract: contract.as_deref(), ty }
174            }
175            Self::Other { contract, ty } => {
176                BorrowedInternalType::Other { contract: contract.as_deref(), ty }
177            }
178        }
179    }
180}
181
182#[derive(Clone, Copy, Debug)]
183pub(crate) enum BorrowedInternalType<'a> {
184    AddressPayable(&'a str),
185    Contract(&'a str),
186    Enum { contract: Option<&'a str>, ty: &'a str },
187    Struct { contract: Option<&'a str>, ty: &'a str },
188    Other { contract: Option<&'a str>, ty: &'a str },
189}
190
191impl fmt::Display for BorrowedInternalType<'_> {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        match *self {
194            Self::AddressPayable(s) => f.write_str(s),
195            Self::Contract(s) => {
196                f.write_str("contract ")?;
197                f.write_str(s)
198            }
199            Self::Enum { contract, ty }
200            | Self::Struct { contract, ty }
201            | Self::Other { contract, ty } => {
202                match self {
203                    Self::Enum { .. } => f.write_str("enum ")?,
204                    Self::Struct { .. } => f.write_str("struct ")?,
205                    Self::Other { .. } => {}
206                    _ => unreachable!(),
207                }
208                if let Some(c) = contract {
209                    f.write_str(c)?;
210                    f.write_str(".")?;
211                }
212                f.write_str(ty)
213            }
214        }
215    }
216}
217
218impl Serialize for BorrowedInternalType<'_> {
219    #[inline]
220    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
221        serializer.collect_str(self)
222    }
223}
224
225impl<'de: 'a, 'a> Deserialize<'de> for BorrowedInternalType<'a> {
226    #[inline]
227    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
228        deserializer.deserialize_str(BorrowedItVisitor)
229    }
230}
231
232impl<'a> BorrowedInternalType<'a> {
233    /// Instantiate a borrowed internal type by parsing a string.
234    fn parse(v: &'a str) -> Option<Self> {
235        if v.starts_with("address payable") {
236            return Some(Self::AddressPayable(v));
237        }
238        if let Some(body) = v.strip_prefix("enum ") {
239            if let Some((contract, ty)) = body.split_once('.') {
240                Some(Self::Enum { contract: Some(contract), ty })
241            } else {
242                Some(Self::Enum { contract: None, ty: body })
243            }
244        } else if let Some(body) = v.strip_prefix("struct ") {
245            if let Some((contract, ty)) = body.split_once('.') {
246                Some(Self::Struct { contract: Some(contract), ty })
247            } else {
248                Some(Self::Struct { contract: None, ty: body })
249            }
250        } else if let Some(body) = v.strip_prefix("contract ") {
251            Some(Self::Contract(body))
252        } else if let Some((contract, ty)) = v.split_once('.') {
253            Some(Self::Other { contract: Some(contract), ty })
254        } else {
255            Some(Self::Other { contract: None, ty: v })
256        }
257    }
258
259    pub(crate) fn into_owned(self) -> InternalType {
260        match self {
261            Self::AddressPayable(s) => InternalType::AddressPayable(s.to_string()),
262            Self::Contract(s) => InternalType::Contract(s.to_string()),
263            Self::Enum { contract, ty } => {
264                InternalType::Enum { contract: contract.map(String::from), ty: ty.to_string() }
265            }
266            Self::Struct { contract, ty } => {
267                InternalType::Struct { contract: contract.map(String::from), ty: ty.to_string() }
268            }
269            Self::Other { contract, ty } => {
270                InternalType::Other { contract: contract.map(String::from), ty: ty.to_string() }
271            }
272        }
273    }
274}
275
276const VISITOR_EXPECTED: &str = "a valid internal type";
277
278pub(crate) struct ItVisitor;
279
280impl Visitor<'_> for ItVisitor {
281    type Value = InternalType;
282
283    fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284        f.write_str(VISITOR_EXPECTED)
285    }
286
287    fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
288        BorrowedInternalType::parse(v)
289            .map(BorrowedInternalType::into_owned)
290            .ok_or_else(|| E::invalid_value(serde::de::Unexpected::Str(v), &VISITOR_EXPECTED))
291    }
292}
293
294const BORROWED_VISITOR_EXPECTED: &str = "a valid borrowed internal type";
295
296pub(crate) struct BorrowedItVisitor;
297
298impl<'de> Visitor<'de> for BorrowedItVisitor {
299    type Value = BorrowedInternalType<'de>;
300
301    fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302        f.write_str(BORROWED_VISITOR_EXPECTED)
303    }
304
305    fn visit_borrowed_str<E: serde::de::Error>(self, v: &'de str) -> Result<Self::Value, E> {
306        BorrowedInternalType::parse(v).ok_or_else(|| {
307            E::invalid_value(serde::de::Unexpected::Str(v), &BORROWED_VISITOR_EXPECTED)
308        })
309    }
310
311    fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
312        Err(E::invalid_value(serde::de::Unexpected::Str(v), &BORROWED_VISITOR_EXPECTED))
313    }
314}
315
316#[cfg(test)]
317mod test {
318    use super::*;
319
320    macro_rules! parser_test {
321        ($test_str:expr, $expected:expr) => {
322            assert_eq!(InternalType::parse($test_str).unwrap(), $expected);
323        };
324    }
325
326    #[test]
327    fn parse_simple_internal_types() {
328        parser_test!(
329            "struct SpentItem[]",
330            InternalType::Struct { contract: None, ty: "SpentItem[]".into() }
331        );
332        parser_test!(
333            "struct Contract.Item",
334            InternalType::Struct { contract: Some("Contract".into()), ty: "Item".into() }
335        );
336        parser_test!(
337            "enum ItemType[32]",
338            InternalType::Enum { contract: None, ty: "ItemType[32]".into() }
339        );
340        parser_test!(
341            "enum Contract.Item",
342            InternalType::Enum { contract: Some("Contract".into()), ty: "Item".into() }
343        );
344
345        parser_test!("contract Item", InternalType::Contract("Item".into()));
346        parser_test!("contract Item[]", InternalType::Contract("Item[]".into()));
347        parser_test!("contract Item[][2]", InternalType::Contract("Item[][2]".into()));
348        parser_test!("contract Item[][2][]", InternalType::Contract("Item[][2][]".into()));
349
350        parser_test!(
351            "address payable",
352            InternalType::AddressPayable("address payable".to_string())
353        );
354        parser_test!(
355            "address payable[][][][][]",
356            InternalType::AddressPayable("address payable[][][][][]".into())
357        );
358        parser_test!("Item", InternalType::Other { contract: None, ty: "Item".into() });
359        parser_test!(
360            "Contract.Item[][33]",
361            InternalType::Other { contract: Some("Contract".into()), ty: "Item[][33]".into() }
362        );
363    }
364}