alloy_sol_type_parser/root.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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
use crate::{ident::identifier_parser, is_valid_identifier, new_input, Error, Input, Result};
use core::fmt;
use winnow::{combinator::trace, stream::Stream, PResult, Parser};
/// A root type, with no array suffixes. Corresponds to a single, non-sequence
/// type. This is the most basic type specifier.
///
/// Note that this type might modify the input string, so [`span()`](Self::span)
/// must not be assumed to be the same as the input string.
///
/// # Examples
///
/// ```
/// # use alloy_sol_type_parser::RootType;
/// let root_type = RootType::parse("uint256")?;
/// assert_eq!(root_type.span(), "uint256");
///
/// // Allows unknown types
/// assert_eq!(RootType::parse("MyStruct")?.span(), "MyStruct");
///
/// // No sequences
/// assert!(RootType::parse("uint256[2]").is_err());
///
/// // No tuples
/// assert!(RootType::parse("(uint256,uint256)").is_err());
///
/// // Input string might get modified
/// assert_eq!(RootType::parse("uint")?.span(), "uint256");
/// # Ok::<_, alloy_sol_type_parser::Error>(())
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct RootType<'a>(&'a str);
impl<'a> TryFrom<&'a str> for RootType<'a> {
type Error = Error;
#[inline]
fn try_from(value: &'a str) -> Result<Self> {
Self::parse(value)
}
}
impl AsRef<str> for RootType<'_> {
#[inline]
fn as_ref(&self) -> &str {
self.0
}
}
impl fmt::Display for RootType<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)
}
}
impl<'a> RootType<'a> {
/// Create a new root type from a string without checking if it's valid.
///
/// # Safety
///
/// The string passed in must be a valid Solidity identifier. See
/// [`is_valid_identifier`].
pub const unsafe fn new_unchecked(s: &'a str) -> Self {
debug_assert!(is_valid_identifier(s));
Self(s)
}
/// Parse a root type from a string.
#[inline]
pub fn parse(input: &'a str) -> Result<Self> {
Self::parser.parse(new_input(input)).map_err(Error::parser)
}
/// [`winnow`] parser for this type.
pub(crate) fn parser(input: &mut Input<'a>) -> PResult<Self> {
trace("RootType", |input: &mut Input<'a>| {
identifier_parser(input).map(|ident| {
// Workaround for enums in library function params or returns.
// See: https://github.com/alloy-rs/core/pull/386
// See ethabi workaround: https://github.com/rust-ethereum/ethabi/blob/b1710adc18f5b771d2d2519c87248b1ba9430778/ethabi/src/param_type/reader.rs#L162-L167
if input.starts_with('.') {
let _ = input.next_token();
let _ = identifier_parser(input);
return Self("uint8");
}
// Normalize the `u?int` aliases to the canonical `u?int256`
match ident {
"uint" => Self("uint256"),
"int" => Self("int256"),
_ => Self(ident),
}
})
})
.parse_next(input)
}
/// The string underlying this type. The type name.
#[inline]
pub const fn span(self) -> &'a str {
self.0
}
/// Returns `Ok(())` if the type is a basic Solidity type.
#[inline]
pub fn try_basic_solidity(self) -> Result<()> {
match self.0 {
"address" | "bool" | "string" | "bytes" | "uint" | "int" | "function" => Ok(()),
name => {
if let Some(sz) = name.strip_prefix("bytes") {
if let Ok(sz) = sz.parse::<usize>() {
if sz != 0 && sz <= 32 {
return Ok(());
}
}
return Err(Error::invalid_size(name));
}
// fast path both integer types
let s = name.strip_prefix('u').unwrap_or(name);
if let Some(sz) = s.strip_prefix("int") {
if let Ok(sz) = sz.parse::<usize>() {
if sz != 0 && sz <= 256 && sz % 8 == 0 {
return Ok(());
}
}
return Err(Error::invalid_size(name));
}
Err(Error::invalid_type_string(name))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn modified_input() {
assert_eq!(RootType::parse("Contract.Enum"), Ok(RootType("uint8")));
assert_eq!(RootType::parse("int"), Ok(RootType("int256")));
assert_eq!(RootType::parse("uint"), Ok(RootType("uint256")));
}
}