use crate::{Address, Bytes, FixedBytes, ParamType, Uint};
use std::fmt;
#[derive(Debug, PartialEq, Clone)]
pub enum Token {
Address(Address),
FixedBytes(FixedBytes),
Bytes(Bytes),
Int(Uint),
Uint(Uint),
Bool(bool),
String(String),
FixedArray(Vec<Token>),
Array(Vec<Token>),
Tuple(Vec<Token>),
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Token::Bool(b) => write!(f, "{}", b),
Token::String(ref s) => write!(f, "{}", s),
Token::Address(ref a) => write!(f, "{:x}", a),
Token::Bytes(ref bytes) | Token::FixedBytes(ref bytes) => write!(f, "{}", hex::encode(&bytes)),
Token::Uint(ref i) | Token::Int(ref i) => write!(f, "{:x}", i),
Token::Array(ref arr) | Token::FixedArray(ref arr) => {
let s = arr.iter().map(|ref t| format!("{}", t)).collect::<Vec<String>>().join(",");
write!(f, "[{}]", s)
}
Token::Tuple(ref s) => {
let s = s.iter().map(|ref t| format!("{}", t)).collect::<Vec<String>>().join(",");
write!(f, "({})", s)
}
}
}
}
impl Token {
pub fn type_check(&self, param_type: &ParamType) -> bool {
match *self {
Token::Address(_) => *param_type == ParamType::Address,
Token::Bytes(_) => *param_type == ParamType::Bytes,
Token::Int(_) => {
matches!(*param_type, ParamType::Int(_))
}
Token::Uint(_) => {
matches!(*param_type, ParamType::Uint(_))
}
Token::Bool(_) => *param_type == ParamType::Bool,
Token::String(_) => *param_type == ParamType::String,
Token::FixedBytes(ref bytes) => {
if let ParamType::FixedBytes(size) = *param_type {
size >= bytes.len()
} else {
false
}
}
Token::Array(ref tokens) => {
if let ParamType::Array(ref param_type) = *param_type {
tokens.iter().all(|t| t.type_check(param_type))
} else {
false
}
}
Token::FixedArray(ref tokens) => {
if let ParamType::FixedArray(ref param_type, size) = *param_type {
size == tokens.len() && tokens.iter().all(|t| t.type_check(param_type))
} else {
false
}
}
Token::Tuple(ref tokens) => {
if let ParamType::Tuple(ref param_type) = *param_type {
tokens.iter().enumerate().all(|(i, t)| t.type_check(¶m_type[i]))
} else {
false
}
}
}
}
pub fn into_address(self) -> Option<Address> {
match self {
Token::Address(address) => Some(address),
_ => None,
}
}
pub fn into_fixed_bytes(self) -> Option<Vec<u8>> {
match self {
Token::FixedBytes(bytes) => Some(bytes),
_ => None,
}
}
pub fn into_bytes(self) -> Option<Vec<u8>> {
match self {
Token::Bytes(bytes) => Some(bytes),
_ => None,
}
}
pub fn into_int(self) -> Option<Uint> {
match self {
Token::Int(int) => Some(int),
_ => None,
}
}
pub fn into_uint(self) -> Option<Uint> {
match self {
Token::Uint(uint) => Some(uint),
_ => None,
}
}
pub fn into_bool(self) -> Option<bool> {
match self {
Token::Bool(b) => Some(b),
_ => None,
}
}
pub fn into_string(self) -> Option<String> {
match self {
Token::String(s) => Some(s),
_ => None,
}
}
pub fn into_fixed_array(self) -> Option<Vec<Token>> {
match self {
Token::FixedArray(arr) => Some(arr),
_ => None,
}
}
pub fn into_array(self) -> Option<Vec<Token>> {
match self {
Token::Array(arr) => Some(arr),
_ => None,
}
}
pub fn types_check(tokens: &[Token], param_types: &[ParamType]) -> bool {
param_types.len() == tokens.len() && {
param_types.iter().zip(tokens).all(|(param_type, token)| token.type_check(param_type))
}
}
pub fn is_dynamic(&self) -> bool {
match self {
Token::Bytes(_) | Token::String(_) | Token::Array(_) => true,
Token::FixedArray(tokens) => tokens.iter().any(|token| token.is_dynamic()),
Token::Tuple(tokens) => tokens.iter().any(|token| token.is_dynamic()),
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use crate::{ParamType, Token};
#[test]
fn test_type_check() {
fn assert_type_check(tokens: Vec<Token>, param_types: Vec<ParamType>) {
assert!(Token::types_check(&tokens, ¶m_types))
}
fn assert_not_type_check(tokens: Vec<Token>, param_types: Vec<ParamType>) {
assert!(!Token::types_check(&tokens, ¶m_types))
}
assert_type_check(vec![Token::Uint(0.into()), Token::Bool(false)], vec![ParamType::Uint(256), ParamType::Bool]);
assert_type_check(vec![Token::Uint(0.into()), Token::Bool(false)], vec![ParamType::Uint(32), ParamType::Bool]);
assert_not_type_check(vec![Token::Uint(0.into())], vec![ParamType::Uint(32), ParamType::Bool]);
assert_not_type_check(vec![Token::Uint(0.into()), Token::Bool(false)], vec![ParamType::Uint(32)]);
assert_not_type_check(
vec![Token::Bool(false), Token::Uint(0.into())],
vec![ParamType::Uint(32), ParamType::Bool],
);
assert_type_check(vec![Token::FixedBytes(vec![0, 0, 0, 0])], vec![ParamType::FixedBytes(4)]);
assert_type_check(vec![Token::FixedBytes(vec![0, 0, 0])], vec![ParamType::FixedBytes(4)]);
assert_not_type_check(vec![Token::FixedBytes(vec![0, 0, 0, 0])], vec![ParamType::FixedBytes(3)]);
assert_type_check(
vec![Token::Array(vec![Token::Bool(false), Token::Bool(true)])],
vec![ParamType::Array(Box::new(ParamType::Bool))],
);
assert_not_type_check(
vec![Token::Array(vec![Token::Bool(false), Token::Uint(0.into())])],
vec![ParamType::Array(Box::new(ParamType::Bool))],
);
assert_not_type_check(
vec![Token::Array(vec![Token::Bool(false), Token::Bool(true)])],
vec![ParamType::Array(Box::new(ParamType::Address))],
);
assert_type_check(
vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])],
vec![ParamType::FixedArray(Box::new(ParamType::Bool), 2)],
);
assert_not_type_check(
vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])],
vec![ParamType::FixedArray(Box::new(ParamType::Bool), 3)],
);
assert_not_type_check(
vec![Token::FixedArray(vec![Token::Bool(false), Token::Uint(0.into())])],
vec![ParamType::FixedArray(Box::new(ParamType::Bool), 2)],
);
assert_not_type_check(
vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])],
vec![ParamType::FixedArray(Box::new(ParamType::Address), 2)],
);
}
#[test]
fn test_is_dynamic() {
assert_eq!(Token::Address("0000000000000000000000000000000000000000".parse().unwrap()).is_dynamic(), false);
assert_eq!(Token::Bytes(vec![0, 0, 0, 0]).is_dynamic(), true);
assert_eq!(Token::FixedBytes(vec![0, 0, 0, 0]).is_dynamic(), false);
assert_eq!(Token::Uint(0.into()).is_dynamic(), false);
assert_eq!(Token::Int(0.into()).is_dynamic(), false);
assert_eq!(Token::Bool(false).is_dynamic(), false);
assert_eq!(Token::String("".into()).is_dynamic(), true);
assert_eq!(Token::Array(vec![Token::Bool(false)]).is_dynamic(), true);
assert_eq!(Token::FixedArray(vec![Token::Uint(0.into())]).is_dynamic(), false);
assert_eq!(Token::FixedArray(vec![Token::String("".into())]).is_dynamic(), true);
assert_eq!(Token::FixedArray(vec![Token::Array(vec![Token::Bool(false)])]).is_dynamic(), true);
}
}