use crate::{spanned, str_parser, Error, Result, TypeSpecifier};
use alloc::vec::Vec;
use winnow::{
combinator::{cut_err, delimited, opt, separated0},
trace::trace,
PResult, Parser,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TupleSpecifier<'a> {
pub span: &'a str,
pub types: Vec<TypeSpecifier<'a>>,
}
impl<'a> TryFrom<&'a str> for TupleSpecifier<'a> {
type Error = Error;
#[inline]
fn try_from(value: &'a str) -> Result<Self> {
Self::parse(value)
}
}
impl AsRef<str> for TupleSpecifier<'_> {
#[inline]
fn as_ref(&self) -> &str {
self.span()
}
}
impl<'a> TupleSpecifier<'a> {
#[inline]
pub fn parse(input: &'a str) -> Result<Self> {
Self::parser.parse(input).map_err(Error::parser)
}
pub(crate) fn parser(input: &mut &'a str) -> PResult<Self> {
trace("TupleSpecifier", spanned(Self::parse_types))
.parse_next(input)
.map(|(span, types)| Self { span, types })
}
#[inline]
fn parse_types(input: &mut &'a str) -> PResult<Vec<TypeSpecifier<'a>>> {
if let Some(stripped) = input.strip_prefix("tuple") {
*input = stripped;
}
trace(
"tuple",
delimited(
str_parser("("),
cut_err(separated0(TypeSpecifier::parser, str_parser(","))),
(opt(","), cut_err(str_parser(")"))),
),
)
.parse_next(input)
}
#[inline]
pub const fn span(&self) -> &'a str {
self.span
}
#[inline]
pub fn try_basic_solidity(&self) -> Result<()> {
self.types
.iter()
.try_for_each(TypeSpecifier::try_basic_solidity)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn extra_close_parens() {
TupleSpecifier::try_from("bool,uint256))").unwrap_err();
}
#[test]
fn extra_open_parents() {
TupleSpecifier::try_from("(bool,uint256").unwrap_err();
}
#[test]
fn nested_tuples() {
assert_eq!(
TupleSpecifier::try_from("(bool,(uint256,uint256))").unwrap(),
TupleSpecifier {
span: "(bool,(uint256,uint256))",
types: vec![
TypeSpecifier::try_from("bool").unwrap(),
TypeSpecifier::try_from("(uint256,uint256)").unwrap(),
]
}
);
assert_eq!(
TupleSpecifier::try_from("(((bool),),)").unwrap(),
TupleSpecifier {
span: "(((bool),),)",
types: vec![TypeSpecifier::try_from("((bool),)").unwrap()]
}
);
}
#[test]
fn does_not_parse_missing_parens() {
TupleSpecifier::try_from("bool,uint256").unwrap_err();
}
}