alloy_sol_type_parser/
tuple.rs

1use crate::{
2    Error, Input, Result, TypeSpecifier, new_input,
3    utils::{spanned, tuple_parser},
4};
5use alloc::vec::Vec;
6use winnow::{
7    ModalResult, Parser,
8    combinator::{opt, preceded, trace},
9};
10
11/// A tuple specifier, with no array suffixes. Corresponds to a sequence of
12/// types.
13///
14/// The internal types are all [`TypeSpecifier`], and may be arbitrarily
15/// complex.
16///
17/// # Examples
18///
19/// ```
20/// # use alloy_sol_type_parser::TupleSpecifier;
21/// let spec = TupleSpecifier::parse("(uint256,uint256)")?;
22/// assert_eq!(spec.span(), "(uint256,uint256)");
23/// assert_eq!(spec.types.len(), 2);
24/// assert_eq!(spec.types[0].span(), "uint256");
25///
26/// // No array suffixes. Use `TypeSpecifier` instead.
27/// assert!(TupleSpecifier::parse("(uint256,uint256)[]").is_err());
28/// # Ok::<_, alloy_sol_type_parser::Error>(())
29/// ```
30#[derive(Clone, Debug, PartialEq, Eq)]
31pub struct TupleSpecifier<'a> {
32    /// The full span of the tuple specifier.
33    pub span: &'a str,
34    /// The internal types.
35    pub types: Vec<TypeSpecifier<'a>>,
36}
37
38impl<'a> TryFrom<&'a str> for TupleSpecifier<'a> {
39    type Error = Error;
40
41    #[inline]
42    fn try_from(value: &'a str) -> Result<Self> {
43        Self::parse(value)
44    }
45}
46
47impl AsRef<str> for TupleSpecifier<'_> {
48    #[inline]
49    fn as_ref(&self) -> &str {
50        self.span()
51    }
52}
53
54impl<'a> TupleSpecifier<'a> {
55    /// Parse a tuple specifier from a string.
56    #[inline]
57    pub fn parse(input: &'a str) -> Result<Self> {
58        Self::parser.parse(new_input(input)).map_err(Error::parser)
59    }
60
61    /// [`winnow`] parser for this type.
62    pub(crate) fn parser(input: &mut Input<'a>) -> ModalResult<Self> {
63        trace("TupleSpecifier", spanned(Self::parse_types))
64            .parse_next(input)
65            .map(|(span, types)| Self { span, types })
66    }
67
68    #[inline]
69    fn parse_types(input: &mut Input<'a>) -> ModalResult<Vec<TypeSpecifier<'a>>> {
70        preceded(opt("tuple"), tuple_parser(TypeSpecifier::parser)).parse_next(input)
71    }
72
73    /// [`winnow`] parser for EIP-712 types.
74    #[cfg(feature = "eip712")]
75    pub(crate) fn eip712_parser(input: &mut Input<'a>) -> ModalResult<Self> {
76        trace("TupleSpecifier::eip712", spanned(Self::parse_eip712_types))
77            .parse_next(input)
78            .map(|(span, types)| Self { span, types })
79    }
80
81    #[cfg(feature = "eip712")]
82    #[inline]
83    fn parse_eip712_types(input: &mut Input<'a>) -> ModalResult<Vec<TypeSpecifier<'a>>> {
84        preceded(opt("tuple"), tuple_parser(TypeSpecifier::eip712_parser)).parse_next(input)
85    }
86
87    /// Returns the tuple specifier as a string.
88    #[inline]
89    pub const fn span(&self) -> &'a str {
90        self.span
91    }
92
93    /// Returns true if the type is a basic Solidity type.
94    #[inline]
95    pub fn try_basic_solidity(&self) -> Result<()> {
96        self.types.iter().try_for_each(TypeSpecifier::try_basic_solidity)
97    }
98}
99
100#[cfg(test)]
101mod test {
102    use super::*;
103
104    #[test]
105    fn extra_close_parens() {
106        TupleSpecifier::parse("bool,uint256))").unwrap_err();
107    }
108
109    #[test]
110    fn extra_open_parents() {
111        TupleSpecifier::parse("(bool,uint256").unwrap_err();
112    }
113
114    #[test]
115    fn nested_tuples() {
116        assert_eq!(
117            TupleSpecifier::parse("(bool,(uint256,uint256))").unwrap(),
118            TupleSpecifier {
119                span: "(bool,(uint256,uint256))",
120                types: vec![
121                    TypeSpecifier::parse("bool").unwrap(),
122                    TypeSpecifier::parse("(uint256,uint256)").unwrap(),
123                ]
124            }
125        );
126        assert_eq!(
127            TupleSpecifier::parse("(((bool),),)").unwrap(),
128            TupleSpecifier {
129                span: "(((bool),),)",
130                types: vec![TypeSpecifier::parse("((bool),)").unwrap()]
131            }
132        );
133    }
134
135    #[test]
136    fn does_not_parse_missing_parens() {
137        TupleSpecifier::parse("bool,uint256").unwrap_err();
138    }
139
140    #[test]
141    fn stack_overflow() {
142        let s = "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((";
143        TupleSpecifier::parse(s).unwrap_err();
144    }
145}