use std::{collections::HashMap, iter::zip};
use fuel_abi_types::{
program_abi::{TypeApplication, TypeDeclaration},
utils::{
extract_array_len, extract_custom_type_name, extract_generic_name, extract_str_len,
has_tuple_format,
},
};
use itertools::Itertools;
use strum_macros::EnumString;
use crate::{
constants::WORD_SIZE,
enum_variants::EnumVariants,
errors::{error, Error, Result},
};
#[derive(Debug, Clone, EnumString, PartialEq, Eq, Default)]
#[strum(ascii_case_insensitive)]
pub enum ParamType {
#[default]
U8,
U16,
U32,
U64,
Bool,
Byte,
B256,
Unit,
Array(Box<ParamType>, usize),
Vector(Box<ParamType>),
#[strum(serialize = "str")]
String(usize),
#[strum(disabled)]
Struct {
name: String,
fields: Vec<(String, ParamType)>,
generics: Vec<ParamType>,
},
#[strum(disabled)]
Enum {
name: String,
variants: EnumVariants,
generics: Vec<ParamType>,
},
Tuple(Vec<ParamType>),
RawSlice,
}
pub enum ReturnLocation {
Return,
ReturnData,
}
impl ParamType {
pub fn get_return_location(&self) -> ReturnLocation {
match self {
Self::Unit | Self::U8 | Self::U16 | Self::U32 | Self::U64 | Self::Bool => {
ReturnLocation::Return
}
_ => ReturnLocation::ReturnData,
}
}
pub fn compute_encoding_width(&self) -> usize {
const fn count_words(bytes: usize) -> usize {
let q = bytes / WORD_SIZE;
let r = bytes % WORD_SIZE;
match r == 0 {
true => q,
false => q + 1,
}
}
match &self {
ParamType::Unit
| ParamType::U8
| ParamType::U16
| ParamType::U32
| ParamType::U64
| ParamType::Bool
| ParamType::Byte => 1,
ParamType::Vector(_) => 3,
ParamType::B256 => 4,
ParamType::Array(param, count) => param.compute_encoding_width() * count,
ParamType::String(len) => count_words(*len),
ParamType::Struct { fields, .. } => {
fields.iter().map(|(_, p)| p.compute_encoding_width()).sum()
}
ParamType::Enum { variants, .. } => variants.compute_encoding_width_of_enum(),
ParamType::Tuple(params) => params.iter().map(|p| p.compute_encoding_width()).sum(),
ParamType::RawSlice => unimplemented!(
"Raw slices are not supported as inputs, so needing the encoding width of a RawSlice should not happen."
),
}
}
pub fn try_from_type_application(
type_application: &TypeApplication,
type_lookup: &HashMap<usize, TypeDeclaration>,
) -> Result<Self> {
Type::from(type_application, type_lookup).try_into()
}
}
#[derive(Debug, Clone)]
struct Type {
type_field: String,
generic_params: Vec<Type>,
components: Vec<Type>,
}
impl Type {
pub fn from(
type_application: &TypeApplication,
type_lookup: &HashMap<usize, TypeDeclaration>,
) -> Self {
Self::resolve(type_application, type_lookup, &[])
}
fn resolve(
type_application: &TypeApplication,
type_lookup: &HashMap<usize, TypeDeclaration>,
parent_generic_params: &[(usize, Type)],
) -> Self {
let type_decl = type_lookup.get(&type_application.type_id).unwrap();
if extract_generic_name(&type_decl.type_field).is_some() {
let (_, generic_type) = parent_generic_params
.iter()
.find(|(id, _)| *id == type_application.type_id)
.unwrap();
return generic_type.clone();
}
let generic_params_lookup =
Self::determine_generics_for_type(type_application, type_lookup, parent_generic_params);
let components = type_decl
.components
.iter()
.flatten()
.map(|component| Self::resolve(component, type_lookup, &generic_params_lookup))
.collect_vec();
Type {
type_field: type_decl.type_field.clone(),
components,
generic_params: generic_params_lookup
.into_iter()
.map(|(_, ty)| ty)
.collect(),
}
}
fn determine_generics_for_type(
type_application: &TypeApplication,
types: &HashMap<usize, TypeDeclaration>,
parent_generic_params: &[(usize, Type)],
) -> Vec<(usize, Self)> {
let type_decl = types.get(&type_application.type_id).unwrap();
match &type_decl.type_parameters {
Some(params) if !params.is_empty() => {
let generic_params_from_current_type = type_application
.type_arguments
.iter()
.flatten()
.map(|ty| Self::resolve(ty, types, parent_generic_params))
.collect_vec();
let generics_to_use = if !generic_params_from_current_type.is_empty() {
generic_params_from_current_type
} else {
parent_generic_params
.iter()
.map(|(_, ty)| ty)
.cloned()
.collect()
};
zip(params.clone(), generics_to_use).collect()
}
_ => parent_generic_params.to_vec(),
}
}
}
impl TryFrom<Type> for ParamType {
type Error = Error;
fn try_from(value: Type) -> Result<Self> {
(&value).try_into()
}
}
impl TryFrom<&Type> for ParamType {
type Error = Error;
fn try_from(the_type: &Type) -> Result<Self> {
let matched_param_type = [
try_primitive,
try_array,
try_str,
try_tuple,
try_vector,
try_enum,
try_struct,
]
.into_iter()
.map(|fun| fun(the_type))
.flat_map(|result| result.ok().flatten())
.next();
matched_param_type.map(Ok).unwrap_or_else(|| {
Err(error!(
InvalidType,
"Type {} couldn't be converted into a ParamType", the_type.type_field
))
})
}
}
fn named_param_types(coll: &[Type]) -> Result<Vec<(String, ParamType)>> {
coll.iter()
.map(|e| Ok(("unused".to_string(), e.try_into()?)))
.collect()
}
fn try_struct(the_type: &Type) -> Result<Option<ParamType>> {
let result = if has_struct_format(&the_type.type_field) {
let generics = param_types(&the_type.generic_params)?;
let fields = named_param_types(&the_type.components)?;
Some(ParamType::Struct {
name: "not_used".to_string(),
fields,
generics,
})
} else {
None
};
Ok(result)
}
fn has_struct_format(field: &str) -> bool {
field.starts_with("struct ")
}
fn try_vector(the_type: &Type) -> Result<Option<ParamType>> {
let type_field = &the_type.type_field;
if has_struct_format(type_field)
&& extract_custom_type_name(type_field).ok_or_else(|| {
error!(
InvalidType,
"Could not extract struct name from type_field {type_field}"
)
})? == "Vec"
{
if the_type.generic_params.len() != 1 {
return Err(error!(
InvalidType,
"Vec must have exactly one generic argument for its type. Found: {:?}",
the_type.generic_params
));
}
let (_, vec_elem_type) = named_param_types(&the_type.generic_params)?.remove(0);
return Ok(Some(ParamType::Vector(Box::new(vec_elem_type))));
}
Ok(None)
}
fn try_enum(the_type: &Type) -> Result<Option<ParamType>> {
let field = &the_type.type_field;
let result = if field.starts_with("enum ") {
let generics = param_types(&the_type.generic_params)?;
let components = named_param_types(&the_type.components)?;
let variants = EnumVariants::new(components)?;
Some(ParamType::Enum {
name: "".to_string(),
variants,
generics,
})
} else {
None
};
Ok(result)
}
fn try_tuple(the_type: &Type) -> Result<Option<ParamType>> {
let result = if has_tuple_format(&the_type.type_field) {
let tuple_elements = param_types(&the_type.components)?;
Some(ParamType::Tuple(tuple_elements))
} else {
None
};
Ok(result)
}
fn param_types(coll: &[Type]) -> Result<Vec<ParamType>> {
coll.iter().map(|e| e.try_into()).collect()
}
fn try_str(the_type: &Type) -> Result<Option<ParamType>> {
Ok(extract_str_len(&the_type.type_field).map(ParamType::String))
}
fn try_array(the_type: &Type) -> Result<Option<ParamType>> {
if let Some(len) = extract_array_len(&the_type.type_field) {
if the_type.components.len() != 1 {}
return match the_type.components.as_slice() {
[single_type] => {
let array_type = single_type.try_into()?;
Ok(Some(ParamType::Array(Box::new(array_type), len)))
}
_ => Err(error!(
InvalidType,
"An array must have elements of exactly one type. Array types: {:?}",
the_type.components
)),
};
}
Ok(None)
}
fn try_primitive(the_type: &Type) -> Result<Option<ParamType>> {
let result = match the_type.type_field.as_str() {
"byte" => Some(ParamType::Byte),
"bool" => Some(ParamType::Bool),
"u8" => Some(ParamType::U8),
"u16" => Some(ParamType::U16),
"u32" => Some(ParamType::U32),
"u64" => Some(ParamType::U64),
"b256" => Some(ParamType::B256),
"()" => Some(ParamType::Unit),
_ => None,
};
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::param_types::ParamType;
const WIDTH_OF_B256: usize = 4;
const WIDTH_OF_U32: usize = 1;
const WIDTH_OF_BOOL: usize = 1;
fn generate_unused_field_names(types: Vec<ParamType>) -> Vec<(String, ParamType)> {
zip(std::iter::repeat("unused".to_string()), types).collect()
}
#[test]
fn array_size_dependent_on_num_of_elements() {
const NUM_ELEMENTS: usize = 11;
let param = ParamType::Array(Box::new(ParamType::B256), NUM_ELEMENTS);
let width = param.compute_encoding_width();
let expected = NUM_ELEMENTS * WIDTH_OF_B256;
assert_eq!(expected, width);
}
#[test]
fn string_size_dependent_on_num_of_elements() {
const NUM_ASCII_CHARS: usize = 9;
let param = ParamType::String(NUM_ASCII_CHARS);
let width = param.compute_encoding_width();
assert_eq!(2, width);
}
#[test]
fn structs_are_just_all_elements_combined() {
let inner_struct = ParamType::Struct {
name: "".to_string(),
fields: generate_unused_field_names(vec![ParamType::U32, ParamType::U32]),
generics: vec![],
};
let a_struct = ParamType::Struct {
name: "".to_string(),
fields: generate_unused_field_names(vec![
ParamType::B256,
ParamType::Bool,
inner_struct,
]),
generics: vec![],
};
let width = a_struct.compute_encoding_width();
const INNER_STRUCT_WIDTH: usize = WIDTH_OF_U32 * 2;
const EXPECTED_WIDTH: usize = WIDTH_OF_B256 + WIDTH_OF_BOOL + INNER_STRUCT_WIDTH;
assert_eq!(EXPECTED_WIDTH, width);
}
#[test]
fn enums_are_as_big_as_their_biggest_variant_plus_a_word() -> Result<()> {
let inner_struct = ParamType::Struct {
name: "".to_string(),
fields: generate_unused_field_names(vec![ParamType::B256]),
generics: vec![],
};
let param = ParamType::Enum {
name: "".to_string(),
variants: EnumVariants::new(generate_unused_field_names(vec![
ParamType::U32,
inner_struct,
]))?,
generics: vec![],
};
let width = param.compute_encoding_width();
const INNER_STRUCT_SIZE: usize = WIDTH_OF_B256;
const EXPECTED_WIDTH: usize = INNER_STRUCT_SIZE + 1;
assert_eq!(EXPECTED_WIDTH, width);
Ok(())
}
#[test]
fn tuples_are_just_all_elements_combined() {
let inner_tuple = ParamType::Tuple(vec![ParamType::B256]);
let param = ParamType::Tuple(vec![ParamType::U32, inner_tuple]);
let width = param.compute_encoding_width();
const INNER_TUPLE_WIDTH: usize = WIDTH_OF_B256;
const EXPECTED_WIDTH: usize = WIDTH_OF_U32 + INNER_TUPLE_WIDTH;
assert_eq!(EXPECTED_WIDTH, width);
}
#[test]
fn handles_simple_types() -> Result<()> {
let parse_param_type = |type_field: &str| {
let type_application = TypeApplication {
name: "".to_string(),
type_id: 0,
type_arguments: None,
};
let declarations = [TypeDeclaration {
type_id: 0,
type_field: type_field.to_string(),
components: None,
type_parameters: None,
}];
let type_lookup = declarations
.into_iter()
.map(|decl| (decl.type_id, decl))
.collect::<HashMap<_, _>>();
ParamType::try_from_type_application(&type_application, &type_lookup)
};
assert_eq!(parse_param_type("u8")?, ParamType::U8);
assert_eq!(parse_param_type("u16")?, ParamType::U16);
assert_eq!(parse_param_type("u32")?, ParamType::U32);
assert_eq!(parse_param_type("u64")?, ParamType::U64);
assert_eq!(parse_param_type("bool")?, ParamType::Bool);
assert_eq!(parse_param_type("byte")?, ParamType::Byte);
assert_eq!(parse_param_type("b256")?, ParamType::B256);
assert_eq!(parse_param_type("()")?, ParamType::Unit);
assert_eq!(parse_param_type("str[21]")?, ParamType::String(21));
Ok(())
}
#[test]
fn handles_arrays() -> Result<()> {
let type_application = TypeApplication {
name: "".to_string(),
type_id: 0,
type_arguments: None,
};
let declarations = [
TypeDeclaration {
type_id: 0,
type_field: "[_; 10]".to_string(),
components: Some(vec![TypeApplication {
name: "__array_element".to_string(),
type_id: 1,
type_arguments: None,
}]),
type_parameters: None,
},
TypeDeclaration {
type_id: 1,
type_field: "u8".to_string(),
components: None,
type_parameters: None,
},
];
let type_lookup = declarations
.into_iter()
.map(|decl| (decl.type_id, decl))
.collect::<HashMap<_, _>>();
let result = ParamType::try_from_type_application(&type_application, &type_lookup)?;
assert_eq!(result, ParamType::Array(Box::new(ParamType::U8), 10));
Ok(())
}
#[test]
fn handles_vectors() -> Result<()> {
let declarations = [
TypeDeclaration {
type_id: 1,
type_field: "generic T".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 2,
type_field: "raw untyped ptr".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 3,
type_field: "struct RawVec".to_string(),
components: Some(vec![
TypeApplication {
name: "ptr".to_string(),
type_id: 2,
type_arguments: None,
},
TypeApplication {
name: "cap".to_string(),
type_id: 5,
type_arguments: None,
},
]),
type_parameters: Some(vec![1]),
},
TypeDeclaration {
type_id: 4,
type_field: "struct Vec".to_string(),
components: Some(vec![
TypeApplication {
name: "buf".to_string(),
type_id: 3,
type_arguments: Some(vec![TypeApplication {
name: "".to_string(),
type_id: 1,
type_arguments: None,
}]),
},
TypeApplication {
name: "len".to_string(),
type_id: 5,
type_arguments: None,
},
]),
type_parameters: Some(vec![1]),
},
TypeDeclaration {
type_id: 5,
type_field: "u64".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 6,
type_field: "u8".to_string(),
components: None,
type_parameters: None,
},
];
let type_application = TypeApplication {
name: "arg".to_string(),
type_id: 4,
type_arguments: Some(vec![TypeApplication {
name: "".to_string(),
type_id: 6,
type_arguments: None,
}]),
};
let type_lookup = declarations
.into_iter()
.map(|decl| (decl.type_id, decl))
.collect::<HashMap<_, _>>();
let result = ParamType::try_from_type_application(&type_application, &type_lookup)?;
assert_eq!(result, ParamType::Vector(Box::new(ParamType::U8)));
Ok(())
}
#[test]
fn handles_structs() -> Result<()> {
let declarations = [
TypeDeclaration {
type_id: 1,
type_field: "generic T".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 2,
type_field: "struct SomeStruct".to_string(),
components: Some(vec![TypeApplication {
name: "field".to_string(),
type_id: 1,
type_arguments: None,
}]),
type_parameters: Some(vec![1]),
},
TypeDeclaration {
type_id: 3,
type_field: "u8".to_string(),
components: None,
type_parameters: None,
},
];
let type_application = TypeApplication {
name: "arg".to_string(),
type_id: 2,
type_arguments: Some(vec![TypeApplication {
name: "".to_string(),
type_id: 3,
type_arguments: None,
}]),
};
let type_lookup = declarations
.into_iter()
.map(|decl| (decl.type_id, decl))
.collect::<HashMap<_, _>>();
let result = ParamType::try_from_type_application(&type_application, &type_lookup)?;
assert_eq!(
result,
ParamType::Struct {
name: "not_used".to_string(),
fields: generate_unused_field_names(vec![ParamType::U8]),
generics: vec![ParamType::U8]
}
);
Ok(())
}
#[test]
fn handles_enums() -> Result<()> {
let declarations = [
TypeDeclaration {
type_id: 1,
type_field: "generic T".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 2,
type_field: "enum SomeEnum".to_string(),
components: Some(vec![TypeApplication {
name: "variant".to_string(),
type_id: 1,
type_arguments: None,
}]),
type_parameters: Some(vec![1]),
},
TypeDeclaration {
type_id: 3,
type_field: "u8".to_string(),
components: None,
type_parameters: None,
},
];
let type_application = TypeApplication {
name: "arg".to_string(),
type_id: 2,
type_arguments: Some(vec![TypeApplication {
name: "".to_string(),
type_id: 3,
type_arguments: None,
}]),
};
let type_lookup = declarations
.into_iter()
.map(|decl| (decl.type_id, decl))
.collect::<HashMap<_, _>>();
let result = ParamType::try_from_type_application(&type_application, &type_lookup)?;
assert_eq!(
result,
ParamType::Enum {
name: "".to_string(),
variants: EnumVariants::new(generate_unused_field_names(vec![ParamType::U8]))?,
generics: vec![ParamType::U8]
}
);
Ok(())
}
#[test]
fn handles_tuples() -> Result<()> {
let declarations = [
TypeDeclaration {
type_id: 1,
type_field: "(_, _)".to_string(),
components: Some(vec![
TypeApplication {
name: "__tuple_element".to_string(),
type_id: 3,
type_arguments: None,
},
TypeApplication {
name: "__tuple_element".to_string(),
type_id: 2,
type_arguments: None,
},
]),
type_parameters: None,
},
TypeDeclaration {
type_id: 2,
type_field: "str[15]".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 3,
type_field: "u8".to_string(),
components: None,
type_parameters: None,
},
];
let type_application = TypeApplication {
name: "arg".to_string(),
type_id: 1,
type_arguments: None,
};
let type_lookup = declarations
.into_iter()
.map(|decl| (decl.type_id, decl))
.collect::<HashMap<_, _>>();
let result = ParamType::try_from_type_application(&type_application, &type_lookup)?;
assert_eq!(
result,
ParamType::Tuple(vec![ParamType::U8, ParamType::String(15)])
);
Ok(())
}
#[test]
fn ultimate_example() -> Result<()> {
let declarations = [
TypeDeclaration {
type_id: 1,
type_field: "(_, _)".to_string(),
components: Some(vec![
TypeApplication {
name: "__tuple_element".to_string(),
type_id: 11,
type_arguments: None,
},
TypeApplication {
name: "__tuple_element".to_string(),
type_id: 11,
type_arguments: None,
},
]),
type_parameters: None,
},
TypeDeclaration {
type_id: 2,
type_field: "(_, _)".to_string(),
components: Some(vec![
TypeApplication {
name: "__tuple_element".to_string(),
type_id: 4,
type_arguments: None,
},
TypeApplication {
name: "__tuple_element".to_string(),
type_id: 24,
type_arguments: None,
},
]),
type_parameters: None,
},
TypeDeclaration {
type_id: 3,
type_field: "(_, _)".to_string(),
components: Some(vec![
TypeApplication {
name: "__tuple_element".to_string(),
type_id: 5,
type_arguments: None,
},
TypeApplication {
name: "__tuple_element".to_string(),
type_id: 13,
type_arguments: None,
},
]),
type_parameters: None,
},
TypeDeclaration {
type_id: 4,
type_field: "[_; 1]".to_string(),
components: Some(vec![TypeApplication {
name: "__array_element".to_string(),
type_id: 8,
type_arguments: Some(vec![TypeApplication {
name: "".to_string(),
type_id: 22,
type_arguments: Some(vec![TypeApplication {
name: "".to_string(),
type_id: 21,
type_arguments: Some(vec![TypeApplication {
name: "".to_string(),
type_id: 18,
type_arguments: Some(vec![TypeApplication {
name: "".to_string(),
type_id: 13,
type_arguments: None,
}]),
}]),
}]),
}]),
}]),
type_parameters: None,
},
TypeDeclaration {
type_id: 5,
type_field: "[_; 2]".to_string(),
components: Some(vec![TypeApplication {
name: "__array_element".to_string(),
type_id: 14,
type_arguments: None,
}]),
type_parameters: None,
},
TypeDeclaration {
type_id: 6,
type_field: "[_; 2]".to_string(),
components: Some(vec![TypeApplication {
name: "__array_element".to_string(),
type_id: 10,
type_arguments: None,
}]),
type_parameters: None,
},
TypeDeclaration {
type_id: 7,
type_field: "b256".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 8,
type_field: "enum EnumWGeneric".to_string(),
components: Some(vec![
TypeApplication {
name: "a".to_string(),
type_id: 25,
type_arguments: None,
},
TypeApplication {
name: "b".to_string(),
type_id: 12,
type_arguments: None,
},
]),
type_parameters: Some(vec![12]),
},
TypeDeclaration {
type_id: 9,
type_field: "generic K".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 10,
type_field: "generic L".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 11,
type_field: "generic M".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 12,
type_field: "generic N".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 13,
type_field: "generic T".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 14,
type_field: "generic U".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 15,
type_field: "raw untyped ptr".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 16,
type_field: "str[2]".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 17,
type_field: "struct MegaExample".to_string(),
components: Some(vec![
TypeApplication {
name: "a".to_string(),
type_id: 3,
type_arguments: None,
},
TypeApplication {
name: "b".to_string(),
type_id: 23,
type_arguments: Some(vec![TypeApplication {
name: "".to_string(),
type_id: 2,
type_arguments: None,
}]),
},
]),
type_parameters: Some(vec![13, 14]),
},
TypeDeclaration {
type_id: 18,
type_field: "struct PassTheGenericOn".to_string(),
components: Some(vec![TypeApplication {
name: "one".to_string(),
type_id: 20,
type_arguments: Some(vec![TypeApplication {
name: "".to_string(),
type_id: 9,
type_arguments: None,
}]),
}]),
type_parameters: Some(vec![9]),
},
TypeDeclaration {
type_id: 19,
type_field: "struct RawVec".to_string(),
components: Some(vec![
TypeApplication {
name: "ptr".to_string(),
type_id: 15,
type_arguments: None,
},
TypeApplication {
name: "cap".to_string(),
type_id: 25,
type_arguments: None,
},
]),
type_parameters: Some(vec![13]),
},
TypeDeclaration {
type_id: 20,
type_field: "struct SimpleGeneric".to_string(),
components: Some(vec![TypeApplication {
name: "single_generic_param".to_string(),
type_id: 13,
type_arguments: None,
}]),
type_parameters: Some(vec![13]),
},
TypeDeclaration {
type_id: 21,
type_field: "struct StructWArrayGeneric".to_string(),
components: Some(vec![TypeApplication {
name: "a".to_string(),
type_id: 6,
type_arguments: None,
}]),
type_parameters: Some(vec![10]),
},
TypeDeclaration {
type_id: 22,
type_field: "struct StructWTupleGeneric".to_string(),
components: Some(vec![TypeApplication {
name: "a".to_string(),
type_id: 1,
type_arguments: None,
}]),
type_parameters: Some(vec![11]),
},
TypeDeclaration {
type_id: 23,
type_field: "struct Vec".to_string(),
components: Some(vec![
TypeApplication {
name: "buf".to_string(),
type_id: 19,
type_arguments: Some(vec![TypeApplication {
name: "".to_string(),
type_id: 13,
type_arguments: None,
}]),
},
TypeApplication {
name: "len".to_string(),
type_id: 25,
type_arguments: None,
},
]),
type_parameters: Some(vec![13]),
},
TypeDeclaration {
type_id: 24,
type_field: "u32".to_string(),
components: None,
type_parameters: None,
},
TypeDeclaration {
type_id: 25,
type_field: "u64".to_string(),
components: None,
type_parameters: None,
},
];
let type_lookup = declarations
.into_iter()
.map(|decl| (decl.type_id, decl))
.collect::<HashMap<_, _>>();
let type_application = TypeApplication {
name: "arg1".to_string(),
type_id: 17,
type_arguments: Some(vec![
TypeApplication {
name: "".to_string(),
type_id: 16,
type_arguments: None,
},
TypeApplication {
name: "".to_string(),
type_id: 7,
type_arguments: None,
},
]),
};
let result = ParamType::try_from_type_application(&type_application, &type_lookup)?;
let expected_param_type = {
let pass_the_generic_on = ParamType::Struct {
name: "not_used".to_string(),
fields: generate_unused_field_names(vec![ParamType::Struct {
name: "not_used".to_string(),
fields: generate_unused_field_names(vec![ParamType::String(2)]),
generics: vec![ParamType::String(2)],
}]),
generics: vec![ParamType::String(2)],
};
let struct_w_array_generic = ParamType::Struct {
name: "not_used".to_string(),
fields: generate_unused_field_names(vec![ParamType::Array(
Box::from(pass_the_generic_on.clone()),
2,
)]),
generics: vec![pass_the_generic_on],
};
let struct_w_tuple_generic = ParamType::Struct {
name: "not_used".to_string(),
fields: generate_unused_field_names(vec![ParamType::Tuple(vec![
struct_w_array_generic.clone(),
struct_w_array_generic.clone(),
])]),
generics: vec![struct_w_array_generic],
};
ParamType::Struct {
name: "not_used".to_string(),
fields: generate_unused_field_names(vec![
ParamType::Tuple(vec![
ParamType::Array(Box::from(ParamType::B256), 2),
ParamType::String(2),
]),
ParamType::Vector(Box::from(ParamType::Tuple(vec![
ParamType::Array(
Box::from(ParamType::Enum {
name: "".to_string(),
variants: EnumVariants::new(generate_unused_field_names(vec![
ParamType::U64,
struct_w_tuple_generic.clone(),
]))
.unwrap(),
generics: vec![struct_w_tuple_generic],
}),
1,
),
ParamType::U32,
]))),
]),
generics: vec![ParamType::String(2), ParamType::B256],
}
};
assert_eq!(result, expected_param_type);
Ok(())
}
}