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    pub fn try_basic_solidity(&self) -> Result<()> {
95        self.types.iter().try_for_each(TypeSpecifier::try_basic_solidity)
96    }
97}
98
99#[cfg(test)]
100mod test {
101    use super::*;
102
103    #[test]
104    fn extra_close_parens() {
105        TupleSpecifier::parse("bool,uint256))").unwrap_err();
106    }
107
108    #[test]
109    fn extra_open_parents() {
110        TupleSpecifier::parse("(bool,uint256").unwrap_err();
111    }
112
113    #[test]
114    fn nested_tuples() {
115        assert_eq!(
116            TupleSpecifier::parse("(bool,(uint256,uint256))").unwrap(),
117            TupleSpecifier {
118                span: "(bool,(uint256,uint256))",
119                types: vec![
120                    TypeSpecifier::parse("bool").unwrap(),
121                    TypeSpecifier::parse("(uint256,uint256)").unwrap(),
122                ]
123            }
124        );
125        assert_eq!(
126            TupleSpecifier::parse("(((bool),),)").unwrap(),
127            TupleSpecifier {
128                span: "(((bool),),)",
129                types: vec![TypeSpecifier::parse("((bool),)").unwrap()]
130            }
131        );
132    }
133
134    #[test]
135    fn does_not_parse_missing_parens() {
136        TupleSpecifier::parse("bool,uint256").unwrap_err();
137    }
138
139    #[test]
140    fn stack_overflow() {
141        let s = "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((";
142        TupleSpecifier::parse(s).unwrap_err();
143    }
144}