#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::{Error, ParamType};
pub struct Reader;
impl Reader {
pub fn read(name: &str) -> Result<ParamType, Error> {
match name.chars().last() {
Some(')') => {
if !name.starts_with('(') {
return Err(Error::InvalidName(name.to_owned()));
};
let mut subtypes = Vec::new();
let mut subtuples = Vec::new();
let mut nested = 0isize;
let mut top_level_paren_open = 0usize;
let mut last_item = 1;
let mut chars = name.chars().enumerate();
while let Some((mut pos, c)) = chars.next() {
match c {
'(' => {
top_level_paren_open = pos;
nested += 1;
if nested > 1 {
subtuples.push(vec![]);
last_item = pos + 1;
}
}
')' => {
nested -= 1;
if nested < 0 {
return Err(Error::InvalidName(name.to_owned()));
}
else if name[last_item..pos].is_empty() {
last_item = pos + 1;
}
else if nested == 0 {
let sub = &name[last_item..pos];
let subtype = Reader::read(sub)?;
subtypes.push(subtype);
last_item = pos + 1;
}
else if nested > 0 {
loop {
match chars.clone().next() {
Some((_, ',')) | Some((_, ')')) | None => break,
_ => {
chars.next();
pos += 1;
}
}
}
let inner_tuple = &name[top_level_paren_open..=pos];
let subtype = Reader::read(inner_tuple)?;
if nested > 1 {
let mut subtuple = core::mem::take(&mut subtuples[(nested - 2) as usize]);
subtuple.push(subtype);
subtypes.push(ParamType::Tuple(subtuple));
} else {
subtypes.push(subtype);
}
last_item = pos + 1;
}
}
',' => {
if name[last_item..pos].is_empty() {
last_item = pos + 1
}
else if nested == 1 {
let sub = &name[last_item..pos];
let subtype = Reader::read(sub)?;
subtypes.push(subtype);
last_item = pos + 1;
}
else if nested > 1 {
let sub = &name[last_item..pos];
let subtype = Reader::read(sub)?;
subtuples[(nested - 2) as usize].push(subtype);
last_item = pos + 1;
}
}
_ => (),
}
}
return Ok(ParamType::Tuple(subtypes));
}
Some(']') => {
let num: String =
name.chars().rev().skip(1).take_while(|c| *c != '[').collect::<String>().chars().rev().collect();
let count = name.chars().count();
return if num.is_empty() {
let subtype = Reader::read(&name[..count - 2])?;
Ok(ParamType::Array(Box::new(subtype)))
} else {
let len = num.parse().map_err(Error::ParseInt)?;
let subtype = Reader::read(&name[..count - num.len() - 2])?;
Ok(ParamType::FixedArray(Box::new(subtype), len))
};
}
_ => (),
}
let result = match name {
"address" => ParamType::Address,
"bytes" => ParamType::Bytes,
"bool" => ParamType::Bool,
"string" => ParamType::String,
"int" => ParamType::Int(256),
"tuple" => ParamType::Tuple(vec![]),
"uint" => ParamType::Uint(256),
s if s.starts_with("int") => {
let len = s[3..].parse().map_err(Error::ParseInt)?;
ParamType::Int(len)
}
s if s.starts_with("uint") => {
let len = s[4..].parse().map_err(Error::ParseInt)?;
ParamType::Uint(len)
}
s if s.starts_with("bytes") => {
let len = s[5..].parse().map_err(Error::ParseInt)?;
ParamType::FixedBytes(len)
}
_ => ParamType::Uint(8),
};
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::Reader;
#[cfg(not(feature = "std"))]
use crate::no_std_prelude::*;
use crate::ParamType;
#[test]
fn test_read_param() {
assert_eq!(Reader::read("address").unwrap(), ParamType::Address);
assert_eq!(Reader::read("bytes").unwrap(), ParamType::Bytes);
assert_eq!(Reader::read("bytes32").unwrap(), ParamType::FixedBytes(32));
assert_eq!(Reader::read("bool").unwrap(), ParamType::Bool);
assert_eq!(Reader::read("string").unwrap(), ParamType::String);
assert_eq!(Reader::read("int").unwrap(), ParamType::Int(256));
assert_eq!(Reader::read("uint").unwrap(), ParamType::Uint(256));
assert_eq!(Reader::read("int32").unwrap(), ParamType::Int(32));
assert_eq!(Reader::read("uint32").unwrap(), ParamType::Uint(32));
}
#[test]
fn test_read_array_param() {
assert_eq!(Reader::read("address[]").unwrap(), ParamType::Array(Box::new(ParamType::Address)));
assert_eq!(Reader::read("uint[]").unwrap(), ParamType::Array(Box::new(ParamType::Uint(256))));
assert_eq!(Reader::read("bytes[]").unwrap(), ParamType::Array(Box::new(ParamType::Bytes)));
assert_eq!(
Reader::read("bool[][]").unwrap(),
ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Bool))))
);
}
#[test]
fn test_read_fixed_array_param() {
assert_eq!(Reader::read("address[2]").unwrap(), ParamType::FixedArray(Box::new(ParamType::Address), 2));
assert_eq!(Reader::read("bool[17]").unwrap(), ParamType::FixedArray(Box::new(ParamType::Bool), 17));
assert_eq!(
Reader::read("bytes[45][3]").unwrap(),
ParamType::FixedArray(Box::new(ParamType::FixedArray(Box::new(ParamType::Bytes), 45)), 3)
);
}
#[test]
fn test_read_mixed_arrays() {
assert_eq!(
Reader::read("bool[][3]").unwrap(),
ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 3)
);
assert_eq!(
Reader::read("bool[3][]").unwrap(),
ParamType::Array(Box::new(ParamType::FixedArray(Box::new(ParamType::Bool), 3)))
);
}
#[test]
fn test_read_struct_param() {
assert_eq!(
Reader::read("(address,bool)").unwrap(),
ParamType::Tuple(vec![ParamType::Address, ParamType::Bool])
);
assert_eq!(
Reader::read("(bool[3],uint256)").unwrap(),
ParamType::Tuple(vec![ParamType::FixedArray(Box::new(ParamType::Bool), 3), ParamType::Uint(256)])
);
}
#[test]
fn test_read_nested_struct_param() {
assert_eq!(
Reader::read("(address,bool,(bool,uint256))").unwrap(),
ParamType::Tuple(vec![
ParamType::Address,
ParamType::Bool,
ParamType::Tuple(vec![ParamType::Bool, ParamType::Uint(256)])
])
);
}
#[test]
fn test_read_complex_nested_struct_param() {
assert_eq!(
Reader::read("(address,bool,(bool,uint256,(bool,uint256)),(bool,uint256))").unwrap(),
ParamType::Tuple(vec![
ParamType::Address,
ParamType::Bool,
ParamType::Tuple(vec![
ParamType::Bool,
ParamType::Uint(256),
ParamType::Tuple(vec![ParamType::Bool, ParamType::Uint(256)])
]),
ParamType::Tuple(vec![ParamType::Bool, ParamType::Uint(256)])
])
);
}
#[test]
fn test_read_nested_tuple_array_param() {
assert_eq!(
Reader::read("(uint256,bytes32)[]").unwrap(),
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Uint(256), ParamType::FixedBytes(32)])))
)
}
#[test]
fn test_read_inner_tuple_array_param() {
use crate::param_type::Writer;
let abi = "((uint256,bytes32)[],address)";
let read = Reader::read(abi).unwrap();
let param = ParamType::Tuple(vec![
ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Uint(256), ParamType::FixedBytes(32)]))),
ParamType::Address,
]);
assert_eq!(read, param);
assert_eq!(abi, Writer::write(¶m));
}
}