alloy_sol_type_parser/
stem.rs

1use crate::{Error, Input, Result, RootType, TupleSpecifier};
2use winnow::{ModalResult, Parser, combinator::trace};
3
4/// A stem of a Solidity array type. It is either a root type, or a tuple type.
5///
6/// # Examples
7///
8/// ```
9/// # use alloy_sol_type_parser::{TypeStem, RootType, TupleSpecifier};
10/// let stem = TypeStem::parse("uint256")?;
11/// assert_eq!(stem.span(), "uint256");
12/// assert!(matches!(stem, TypeStem::Root(_)));
13/// assert_eq!(stem.as_root(), Some(&RootType::parse("uint256").unwrap()));
14///
15/// let stem = TypeStem::parse("(uint256,bool)")?;
16/// assert_eq!(stem.span(), "(uint256,bool)");
17/// assert!(matches!(stem, TypeStem::Tuple(_)));
18/// assert_eq!(stem.as_tuple(), Some(&TupleSpecifier::parse("(uint256,bool)").unwrap()));
19/// # Ok::<_, alloy_sol_type_parser::Error>(())
20/// ```
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub enum TypeStem<'a> {
23    /// Root type.
24    Root(RootType<'a>),
25    /// Tuple type.
26    Tuple(TupleSpecifier<'a>),
27}
28
29impl<'a> TryFrom<&'a str> for TypeStem<'a> {
30    type Error = Error;
31
32    #[inline]
33    fn try_from(value: &'a str) -> Result<Self> {
34        Self::parse(value)
35    }
36}
37
38impl AsRef<str> for TypeStem<'_> {
39    #[inline]
40    fn as_ref(&self) -> &str {
41        self.span()
42    }
43}
44
45impl<'a> TypeStem<'a> {
46    /// Parse a type stem from a string.
47    #[inline]
48    pub fn parse(input: &'a str) -> Result<Self> {
49        if input.starts_with('(') || input.starts_with("tuple(") {
50            input.try_into().map(Self::Tuple)
51        } else {
52            input.try_into().map(Self::Root)
53        }
54    }
55
56    /// [`winnow`] parser for EIP-712 types.
57    #[cfg(feature = "eip712")]
58    pub(crate) fn eip712_parser(input: &mut Input<'a>) -> ModalResult<Self> {
59        let name = "TypeStem::eip712";
60        if input.starts_with('(') || input.starts_with("tuple(") {
61            trace(name, TupleSpecifier::eip712_parser).parse_next(input).map(Self::Tuple)
62        } else {
63            trace(name, RootType::eip712_parser).parse_next(input).map(Self::Root)
64        }
65    }
66
67    /// [`winnow`] parser for this type.
68    pub(crate) fn parser(input: &mut Input<'a>) -> ModalResult<Self> {
69        let name = "TypeStem";
70        if input.starts_with('(') || input.starts_with("tuple(") {
71            trace(name, TupleSpecifier::parser).parse_next(input).map(Self::Tuple)
72        } else {
73            trace(name, RootType::parser).parse_next(input).map(Self::Root)
74        }
75    }
76
77    /// Fallible conversion to a root type
78    #[inline]
79    pub const fn as_root(&self) -> Option<&RootType<'a>> {
80        match self {
81            Self::Root(root) => Some(root),
82            Self::Tuple(_) => None,
83        }
84    }
85
86    /// Fallible conversion to a tuple type
87    #[inline]
88    pub const fn as_tuple(&self) -> Option<&TupleSpecifier<'a>> {
89        match self {
90            Self::Root(_) => None,
91            Self::Tuple(tuple) => Some(tuple),
92        }
93    }
94
95    /// Returns the type stem as a string.
96    #[inline]
97    pub const fn span(&self) -> &'a str {
98        match self {
99            Self::Root(root) => root.span(),
100            Self::Tuple(tuple) => tuple.span(),
101        }
102    }
103
104    /// Returns true if the type is a basic Solidity type.
105    #[inline]
106    pub fn try_basic_solidity(&self) -> Result<()> {
107        match self {
108            Self::Root(root) => root.try_basic_solidity(),
109            Self::Tuple(tuple) => tuple.try_basic_solidity(),
110        }
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn tuple() {
120        // empty tuple
121        assert_eq!(
122            TypeStem::parse("()"),
123            Ok(TypeStem::Tuple(TupleSpecifier { span: "()", types: vec![] }))
124        );
125        TypeStem::parse("tuple(").unwrap_err();
126        assert_eq!(
127            TypeStem::parse("tuple()"),
128            Ok(TypeStem::Tuple(TupleSpecifier { span: "tuple()", types: vec![] }))
129        );
130
131        // type named tuple
132        assert_eq!(TypeStem::parse("tuple"), Ok(TypeStem::Root(RootType::parse("tuple").unwrap())))
133    }
134}