use crate::{Error, Input, Result, ident::identifier_parser, is_valid_identifier, new_input};
use core::fmt;
use winnow::{ModalResult, Parser, combinator::trace, stream::Stream};
#[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> {
pub const unsafe fn new_unchecked(s: &'a str) -> Self {
debug_assert!(is_valid_identifier(s));
Self(s)
}
#[inline]
pub fn parse(input: &'a str) -> Result<Self> {
Self::parser.parse(new_input(input)).map_err(Error::parser)
}
pub(crate) fn parser(input: &mut Input<'a>) -> ModalResult<Self> {
trace("RootType", |input: &mut Input<'a>| {
identifier_parser(input).map(|ident| {
if input.starts_with('.') {
let _ = input.next_token();
let _ = identifier_parser(input);
return Self("uint8");
}
match ident {
"uint" => Self("uint256"),
"int" => Self("int256"),
_ => Self(ident),
}
})
})
.parse_next(input)
}
#[cfg(feature = "eip712")]
#[inline]
pub fn parse_eip712(input: &'a str) -> Result<Self> {
Self::eip712_parser.parse(new_input(input)).map_err(Error::parser)
}
#[cfg(feature = "eip712")]
pub(crate) fn eip712_parser(input: &mut Input<'a>) -> ModalResult<Self> {
trace("RootType::eip712", |input: &mut Input<'a>| {
use crate::ident::eip712_identifier_parser;
eip712_identifier_parser(input).map(|ident| {
if input.starts_with('.') {
let _ = input.next_token();
let _ = eip712_identifier_parser(input);
return Self("uint8");
}
match ident {
"uint" => Self("uint256"),
"int" => Self("int256"),
_ => Self(ident),
}
})
})
.parse_next(input)
}
#[inline]
pub const fn span(self) -> &'a str {
self.0
}
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));
}
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")));
}
}