use crate::errors::Error;
use pop_common::format_type;
use scale_info::{Field, PortableRegistry, TypeDef, form::PortableForm};
use subxt::Metadata;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Param {
pub name: String,
pub type_name: String,
pub sub_params: Vec<Param>,
pub is_optional: bool,
pub is_tuple: bool,
pub is_variant: bool,
pub is_sequence: bool,
}
pub fn field_to_param(metadata: &Metadata, field: &Field<PortableForm>) -> Result<Param, Error> {
let registry = metadata.types();
if let Some(name) = field.type_name.as_deref() &&
name.contains("RuntimeCall")
{
return Err(Error::CallableNotSupported);
}
let name = field.name.as_deref().unwrap_or("Unnamed"); type_to_param(name, registry, field.ty.id)
}
pub fn type_to_param(
name: &str,
registry: &PortableRegistry,
type_id: u32,
) -> Result<Param, Error> {
let type_info = registry
.resolve(type_id)
.ok_or_else(|| Error::MetadataParsingError(name.to_string()))?;
if type_info.path.segments.contains(&"RuntimeCall".to_string()) {
return Err(Error::CallableNotSupported);
}
for param in &type_info.type_params {
if param.name.contains("RuntimeCall") {
return Err(Error::CallableNotSupported);
}
}
if type_info.path.segments == ["Option"] {
if let Some(sub_type_id) = type_info.type_params.first().and_then(|param| param.ty) {
let sub_param = type_to_param(name, registry, sub_type_id.id)?;
Ok(Param {
name: name.to_string(),
type_name: sub_param.type_name,
sub_params: sub_param.sub_params,
is_optional: true,
..Default::default()
})
} else {
Err(Error::MetadataParsingError(name.to_string()))
}
} else {
let type_name = format_type(type_info, registry);
match &type_info.type_def {
TypeDef::Primitive(_) | TypeDef::Array(_) | TypeDef::Compact(_) =>
Ok(Param { name: name.to_string(), type_name, ..Default::default() }),
TypeDef::Composite(composite) => {
let sub_params = composite
.fields
.iter()
.map(|field| {
type_to_param(field.name.as_deref().unwrap_or(name), registry, field.ty.id)
})
.collect::<Result<Vec<Param>, Error>>()?;
Ok(Param { name: name.to_string(), type_name, sub_params, ..Default::default() })
},
TypeDef::Variant(variant) => {
let variant_params = variant
.variants
.iter()
.map(|variant_param| {
let variant_sub_params = variant_param
.fields
.iter()
.map(|field| {
type_to_param(
field.name.as_deref().unwrap_or(&variant_param.name),
registry,
field.ty.id,
)
})
.collect::<Result<Vec<Param>, Error>>()?;
Ok(Param {
name: variant_param.name.clone(),
type_name: "".to_string(),
sub_params: variant_sub_params,
is_variant: true,
..Default::default()
})
})
.collect::<Result<Vec<Param>, Error>>()?;
Ok(Param {
name: name.to_string(),
type_name,
sub_params: variant_params,
is_variant: true,
..Default::default()
})
},
TypeDef::Sequence(_) => Ok(Param {
name: name.to_string(),
type_name,
is_sequence: true,
..Default::default()
}),
TypeDef::Tuple(tuple) => {
let sub_params = tuple
.fields
.iter()
.enumerate()
.map(|(index, field_id)| {
type_to_param(
&format!("Index {index} of the tuple {name}"),
registry,
field_id.id,
)
})
.collect::<Result<Vec<Param>, Error>>()?;
Ok(Param {
name: name.to_string(),
type_name,
sub_params,
is_tuple: true,
..Default::default()
})
},
_ => Err(Error::MetadataParsingError(name.to_string())),
}
}
}