use std::fmt;
use crate::client::evm::abi::{AbiError, WORD};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Kind {
Uint(u16),
Address,
Bool,
String,
Bytes,
Array(Box<Kind>),
}
const DEEPEST: usize = 8;
impl Kind {
pub fn parse(text: &str) -> Result<Kind, AbiError> {
kind_of(text.trim(), 0).ok_or_else(|| AbiError::Type(text.trim().to_owned()))
}
#[must_use]
pub const fn is_dynamic(&self) -> bool {
matches!(self, Kind::String | Kind::Bytes | Kind::Array(_))
}
pub(super) const fn head(&self) -> usize {
WORD
}
#[must_use]
pub fn canonical(&self) -> String {
match self {
Kind::Uint(bits) => format!("uint{bits}"),
Kind::Address => "address".to_owned(),
Kind::Bool => "bool".to_owned(),
Kind::String => "string".to_owned(),
Kind::Bytes => "bytes".to_owned(),
Kind::Array(inner) => format!("{}[]", inner.canonical()),
}
}
}
impl fmt::Display for Kind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.canonical())
}
}
pub fn kinds(text: &str) -> Result<Vec<Kind>, AbiError> {
let text = text.trim();
if text.is_empty() {
return Ok(Vec::new());
}
text.split(',')
.map(|one| {
let one = one.trim();
let kind = one.split_whitespace().next().unwrap_or_default();
Kind::parse(kind)
})
.collect()
}
fn kind_of(text: &str, depth: usize) -> Option<Kind> {
if depth > DEEPEST {
return None;
}
if let Some(inner) = text.strip_suffix("[]") {
return kind_of(inner, depth + 1).map(|inner| Kind::Array(Box::new(inner)));
}
Some(match text {
"address" => Kind::Address,
"bool" => Kind::Bool,
"string" => Kind::String,
"bytes" => Kind::Bytes,
"uint" => Kind::Uint(256),
_ => {
let bits: u16 = text.strip_prefix("uint")?.parse().ok()?;
(bits > 0 && bits <= 256 && bits.is_multiple_of(8)).then_some(Kind::Uint(bits))?
}
})
}