alloy_sol_type_parser/
stem.rs

1use crate::{Error, Input, Result, RootType, TupleSpecifier};
2use winnow::{combinator::trace, ModalResult, Parser};
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 this type.
57    pub(crate) fn parser(input: &mut Input<'a>) -> ModalResult<Self> {
58        let name = "TypeStem";
59        if input.starts_with('(') || input.starts_with("tuple(") {
60            trace(name, TupleSpecifier::parser).parse_next(input).map(Self::Tuple)
61        } else {
62            trace(name, RootType::parser).parse_next(input).map(Self::Root)
63        }
64    }
65
66    /// Fallible conversion to a root type
67    #[inline]
68    pub const fn as_root(&self) -> Option<&RootType<'a>> {
69        match self {
70            Self::Root(root) => Some(root),
71            Self::Tuple(_) => None,
72        }
73    }
74
75    /// Fallible conversion to a tuple type
76    #[inline]
77    pub const fn as_tuple(&self) -> Option<&TupleSpecifier<'a>> {
78        match self {
79            Self::Root(_) => None,
80            Self::Tuple(tuple) => Some(tuple),
81        }
82    }
83
84    /// Returns the type stem as a string.
85    #[inline]
86    pub const fn span(&self) -> &'a str {
87        match self {
88            Self::Root(root) => root.span(),
89            Self::Tuple(tuple) => tuple.span(),
90        }
91    }
92
93    /// Returns true if the type is a basic Solidity type.
94    #[inline]
95    pub fn try_basic_solidity(&self) -> Result<()> {
96        match self {
97            Self::Root(root) => root.try_basic_solidity(),
98            Self::Tuple(tuple) => tuple.try_basic_solidity(),
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn tuple() {
109        // empty tuple
110        assert_eq!(
111            TypeStem::parse("()"),
112            Ok(TypeStem::Tuple(TupleSpecifier { span: "()", types: vec![] }))
113        );
114        TypeStem::parse("tuple(").unwrap_err();
115        assert_eq!(
116            TypeStem::parse("tuple()"),
117            Ok(TypeStem::Tuple(TupleSpecifier { span: "tuple()", types: vec![] }))
118        );
119
120        // type named tuple
121        assert_eq!(TypeStem::parse("tuple"), Ok(TypeStem::Root(RootType::parse("tuple").unwrap())))
122    }
123}