alloy_sol_type_parser/
stem.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use crate::{Error, Input, Result, RootType, TupleSpecifier};
use winnow::{combinator::trace, PResult, Parser};

/// A stem of a Solidity array type. It is either a root type, or a tuple type.
///
/// # Examples
///
/// ```
/// # use alloy_sol_type_parser::{TypeStem, RootType, TupleSpecifier};
/// let stem = TypeStem::parse("uint256")?;
/// assert_eq!(stem.span(), "uint256");
/// assert!(matches!(stem, TypeStem::Root(_)));
/// assert_eq!(stem.as_root(), Some(&RootType::parse("uint256").unwrap()));
///
/// let stem = TypeStem::parse("(uint256,bool)")?;
/// assert_eq!(stem.span(), "(uint256,bool)");
/// assert!(matches!(stem, TypeStem::Tuple(_)));
/// assert_eq!(stem.as_tuple(), Some(&TupleSpecifier::parse("(uint256,bool)").unwrap()));
/// # Ok::<_, alloy_sol_type_parser::Error>(())
/// ```
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeStem<'a> {
    /// Root type.
    Root(RootType<'a>),
    /// Tuple type.
    Tuple(TupleSpecifier<'a>),
}

impl<'a> TryFrom<&'a str> for TypeStem<'a> {
    type Error = Error;

    #[inline]
    fn try_from(value: &'a str) -> Result<Self> {
        Self::parse(value)
    }
}

impl AsRef<str> for TypeStem<'_> {
    #[inline]
    fn as_ref(&self) -> &str {
        self.span()
    }
}

impl<'a> TypeStem<'a> {
    /// Parse a type stem from a string.
    #[inline]
    pub fn parse(input: &'a str) -> Result<Self> {
        if input.starts_with('(') || input.starts_with("tuple(") {
            input.try_into().map(Self::Tuple)
        } else {
            input.try_into().map(Self::Root)
        }
    }

    /// [`winnow`] parser for this type.
    pub(crate) fn parser(input: &mut Input<'a>) -> PResult<Self> {
        let name = "TypeStem";
        if input.starts_with('(') || input.starts_with("tuple(") {
            trace(name, TupleSpecifier::parser).parse_next(input).map(Self::Tuple)
        } else {
            trace(name, RootType::parser).parse_next(input).map(Self::Root)
        }
    }

    /// Fallible conversion to a root type
    #[inline]
    pub const fn as_root(&self) -> Option<&RootType<'a>> {
        match self {
            Self::Root(root) => Some(root),
            Self::Tuple(_) => None,
        }
    }

    /// Fallible conversion to a tuple type
    #[inline]
    pub const fn as_tuple(&self) -> Option<&TupleSpecifier<'a>> {
        match self {
            Self::Root(_) => None,
            Self::Tuple(tuple) => Some(tuple),
        }
    }

    /// Returns the type stem as a string.
    #[inline]
    pub const fn span(&self) -> &'a str {
        match self {
            Self::Root(root) => root.span(),
            Self::Tuple(tuple) => tuple.span(),
        }
    }

    /// Returns true if the type is a basic Solidity type.
    #[inline]
    pub fn try_basic_solidity(&self) -> Result<()> {
        match self {
            Self::Root(root) => root.try_basic_solidity(),
            Self::Tuple(tuple) => tuple.try_basic_solidity(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn tuple() {
        // empty tuple
        assert_eq!(
            TypeStem::parse("()"),
            Ok(TypeStem::Tuple(TupleSpecifier { span: "()", types: vec![] }))
        );
        TypeStem::parse("tuple(").unwrap_err();
        assert_eq!(
            TypeStem::parse("tuple()"),
            Ok(TypeStem::Tuple(TupleSpecifier { span: "tuple()", types: vec![] }))
        );

        // type named tuple
        assert_eq!(TypeStem::parse("tuple"), Ok(TypeStem::Root(RootType::parse("tuple").unwrap())))
    }
}