alloy_sol_type_parser/
type_spec.rs

1use crate::{
2    new_input,
3    utils::{spanned, str_parser},
4    Error, Input, Result, TypeStem,
5};
6use alloc::vec::Vec;
7use core::num::NonZeroUsize;
8use winnow::{
9    ascii::digit0,
10    combinator::{cut_err, delimited, repeat, trace},
11    error::{ErrMode, FromExternalError},
12    ModalResult, Parser,
13};
14
15/// Represents a type-name. Consists of an identifier and optional array sizes.
16///
17/// A type specifier has a stem, which is [`TypeStem`] representing either a
18/// [`RootType`] or a [`TupleSpecifier`], and a list of array sizes. The array
19/// sizes are in innermost-to-outermost order. An empty array size vec indicates
20/// that the specified type is not an array
21///
22/// Type specifier examples:
23/// - `uint256`
24/// - `uint256[2]`
25/// - `uint256[2][]`
26/// - `(uint256,uint256)`
27/// - `(uint256,uint256)[2]`
28/// - `MyStruct`
29/// - `MyStruct[2]`
30///
31/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.typeName>
32///
33/// [`RootType`]: crate::RootType
34/// [`TupleSpecifier`]: crate::TupleSpecifier
35///
36/// ## Compatibility with JSON ABI
37///
38/// This type supports the `internalType` semantics for JSON-ABI compatibility.
39///
40/// Examples of valid JSON ABI internal types:
41/// - `contract MyContract`
42/// - `struct MyStruct`
43/// - `enum MyEnum`
44/// - `struct MyContract.MyStruct\[333\]`
45/// - `enum MyContract.MyEnum[][][][][][]`
46/// - `MyValueType`
47///
48/// # Examples
49///
50/// ```
51/// # use alloy_sol_type_parser::TypeSpecifier;
52/// # use core::num::NonZeroUsize;
53/// let spec = TypeSpecifier::parse("uint256[2][]")?;
54/// assert_eq!(spec.span(), "uint256[2][]");
55/// assert_eq!(spec.stem.span(), "uint256");
56/// // The sizes are in innermost-to-outermost order.
57/// assert_eq!(spec.sizes.as_slice(), &[NonZeroUsize::new(2), None]);
58/// # Ok::<_, alloy_sol_type_parser::Error>(())
59/// ```
60#[derive(Clone, Debug, PartialEq, Eq)]
61pub struct TypeSpecifier<'a> {
62    /// The full span of the specifier.
63    pub span: &'a str,
64    /// The type stem, which is either a root type or a tuple type.
65    pub stem: TypeStem<'a>,
66    /// Array sizes, in innermost-to-outermost order. If the size is `None`,
67    /// then the array is dynamic. If the size is `Some`, then the array is
68    /// fixed-size. If the vec is empty, then the type is not an array.
69    pub sizes: Vec<Option<NonZeroUsize>>,
70}
71
72impl<'a> TryFrom<&'a str> for TypeSpecifier<'a> {
73    type Error = Error;
74
75    #[inline]
76    fn try_from(s: &'a str) -> Result<Self> {
77        Self::parse(s)
78    }
79}
80
81impl AsRef<str> for TypeSpecifier<'_> {
82    #[inline]
83    fn as_ref(&self) -> &str {
84        self.span()
85    }
86}
87
88impl<'a> TypeSpecifier<'a> {
89    /// Parse a type specifier from a string.
90    #[inline]
91    pub fn parse(s: &'a str) -> Result<Self> {
92        Self::parser.parse(new_input(s)).map_err(Error::parser)
93    }
94
95    /// [`winnow`] parser for this type.
96    pub(crate) fn parser(input: &mut Input<'a>) -> ModalResult<Self> {
97        trace(
98            "TypeSpecifier",
99            spanned(|input: &mut Input<'a>| {
100                let stem = TypeStem::parser(input)?;
101                let sizes = if input.starts_with('[') {
102                    repeat(
103                        1..,
104                        delimited(str_parser("["), array_size_parser, cut_err(str_parser("]"))),
105                    )
106                    .parse_next(input)?
107                } else {
108                    Vec::new()
109                };
110                Ok((stem, sizes))
111            }),
112        )
113        .parse_next(input)
114        .map(|(span, (stem, sizes))| Self { span, stem, sizes })
115    }
116
117    /// Returns the type stem as a string.
118    #[inline]
119    pub const fn span(&self) -> &'a str {
120        self.span
121    }
122
123    /// Returns the type stem.
124    #[inline]
125    pub const fn stem(&self) -> &TypeStem<'_> {
126        &self.stem
127    }
128
129    /// Returns true if the type is a basic Solidity type.
130    #[inline]
131    pub fn try_basic_solidity(&self) -> Result<()> {
132        self.stem.try_basic_solidity()
133    }
134
135    /// Returns true if this type is an array.
136    #[inline]
137    pub fn is_array(&self) -> bool {
138        !self.sizes.is_empty()
139    }
140}
141
142fn array_size_parser(input: &mut Input<'_>) -> ModalResult<Option<NonZeroUsize>> {
143    let digits = digit0(input)?;
144    if digits.is_empty() {
145        return Ok(None);
146    }
147    digits.parse().map(Some).map_err(|e| ErrMode::from_external_error(input, e))
148}
149
150#[cfg(test)]
151mod test {
152    use super::*;
153    use crate::TupleSpecifier;
154    use alloc::string::ToString;
155
156    #[track_caller]
157    fn assert_error_contains(e: &Error, s: &str) {
158        if cfg!(feature = "std") {
159            let es = e.to_string();
160            assert!(es.contains(s), "{s:?} not in {es:?}");
161        }
162    }
163
164    #[test]
165    fn parse_test() {
166        assert_eq!(
167            TypeSpecifier::parse("uint"),
168            Ok(TypeSpecifier {
169                span: "uint",
170                stem: TypeStem::parse("uint256").unwrap(),
171                sizes: vec![],
172            })
173        );
174
175        assert_eq!(
176            TypeSpecifier::parse("uint256"),
177            Ok(TypeSpecifier {
178                span: "uint256",
179                stem: TypeStem::parse("uint256").unwrap(),
180                sizes: vec![],
181            })
182        );
183
184        assert_eq!(
185            TypeSpecifier::parse("uint256[2]"),
186            Ok(TypeSpecifier {
187                span: "uint256[2]",
188                stem: TypeStem::parse("uint256").unwrap(),
189                sizes: vec![NonZeroUsize::new(2)],
190            })
191        );
192
193        assert_eq!(
194            TypeSpecifier::parse("uint256[2][]"),
195            Ok(TypeSpecifier {
196                span: "uint256[2][]",
197                stem: TypeStem::parse("uint256").unwrap(),
198                sizes: vec![NonZeroUsize::new(2), None],
199            })
200        );
201
202        assert_eq!(
203            TypeSpecifier::parse("(uint256,uint256)"),
204            Ok(TypeSpecifier {
205                span: "(uint256,uint256)",
206                stem: TypeStem::Tuple(TupleSpecifier::parse("(uint256,uint256)").unwrap()),
207                sizes: vec![],
208            })
209        );
210
211        assert_eq!(
212            TypeSpecifier::parse("(uint256,uint256)[2]"),
213            Ok(TypeSpecifier {
214                span: "(uint256,uint256)[2]",
215                stem: TypeStem::Tuple(TupleSpecifier::parse("(uint256,uint256)").unwrap()),
216                sizes: vec![NonZeroUsize::new(2)],
217            })
218        );
219
220        assert_eq!(
221            TypeSpecifier::parse("MyStruct"),
222            Ok(TypeSpecifier {
223                span: "MyStruct",
224                stem: TypeStem::parse("MyStruct").unwrap(),
225                sizes: vec![],
226            })
227        );
228
229        assert_eq!(
230            TypeSpecifier::parse("MyStruct[2]"),
231            Ok(TypeSpecifier {
232                span: "MyStruct[2]",
233                stem: TypeStem::parse("MyStruct").unwrap(),
234                sizes: vec![NonZeroUsize::new(2)],
235            })
236        );
237    }
238
239    #[test]
240    fn sizes() {
241        TypeSpecifier::parse("a[").unwrap_err();
242        TypeSpecifier::parse("a[][").unwrap_err();
243
244        assert_eq!(
245            TypeSpecifier::parse("a[]"),
246            Ok(TypeSpecifier {
247                span: "a[]",
248                stem: TypeStem::parse("a").unwrap(),
249                sizes: vec![None],
250            }),
251        );
252
253        assert_eq!(
254            TypeSpecifier::parse("a[1]"),
255            Ok(TypeSpecifier {
256                span: "a[1]",
257                stem: TypeStem::parse("a").unwrap(),
258                sizes: vec![NonZeroUsize::new(1)],
259            }),
260        );
261
262        let e = TypeSpecifier::parse("a[0]").unwrap_err();
263        assert_error_contains(&e, "number would be zero for non-zero type");
264        TypeSpecifier::parse("a[x]").unwrap_err();
265
266        TypeSpecifier::parse("a[ ]").unwrap_err();
267        TypeSpecifier::parse("a[  ]").unwrap_err();
268        TypeSpecifier::parse("a[ 0]").unwrap_err();
269        TypeSpecifier::parse("a[0 ]").unwrap_err();
270
271        TypeSpecifier::parse("a[a]").unwrap_err();
272        TypeSpecifier::parse("a[ a]").unwrap_err();
273        TypeSpecifier::parse("a[a ]").unwrap_err();
274
275        TypeSpecifier::parse("a[ 1]").unwrap_err();
276        TypeSpecifier::parse("a[1 ]").unwrap_err();
277
278        TypeSpecifier::parse(&format!("a[{}]", usize::MAX)).unwrap();
279        let e = TypeSpecifier::parse(&format!("a[{}0]", usize::MAX)).unwrap_err();
280        assert_error_contains(&e, "number too large to fit in target type");
281    }
282
283    #[test]
284    fn try_basic_solidity() {
285        assert_eq!(TypeSpecifier::parse("uint").unwrap().try_basic_solidity(), Ok(()));
286        assert_eq!(TypeSpecifier::parse("int").unwrap().try_basic_solidity(), Ok(()));
287        assert_eq!(TypeSpecifier::parse("uint256").unwrap().try_basic_solidity(), Ok(()));
288        assert_eq!(TypeSpecifier::parse("uint256[]").unwrap().try_basic_solidity(), Ok(()));
289        assert_eq!(TypeSpecifier::parse("(uint256,uint256)").unwrap().try_basic_solidity(), Ok(()));
290        assert_eq!(
291            TypeSpecifier::parse("(uint256,uint256)[2]").unwrap().try_basic_solidity(),
292            Ok(())
293        );
294        assert_eq!(
295            TypeSpecifier::parse("tuple(uint256,uint256)").unwrap().try_basic_solidity(),
296            Ok(())
297        );
298        assert_eq!(
299            TypeSpecifier::parse("tuple(address,bytes,(bool,(string,uint256)[][3]))[2]")
300                .unwrap()
301                .try_basic_solidity(),
302            Ok(())
303        );
304    }
305
306    #[test]
307    fn not_basic_solidity() {
308        assert_eq!(
309            TypeSpecifier::parse("MyStruct").unwrap().try_basic_solidity(),
310            Err(Error::invalid_type_string("MyStruct"))
311        );
312    }
313}