use scale_info::{form::PortableForm, Field, PortableRegistry, Type, TypeDef, TypeParameter};
use std::collections::HashMap;
use crate::TypegenError;
pub fn syn_type_path(ty: &Type<PortableForm>) -> Result<syn::TypePath, TypegenError> {
let joined_path = ty.path.segments.join("::");
let ty_path: syn::TypePath = syn::parse_str(&joined_path)?;
Ok(ty_path)
}
pub fn ensure_unique_type_paths(types: &mut PortableRegistry) {
let mut types_with_same_type_path_grouped_by_shape = HashMap::<&[String], Vec<Vec<u32>>>::new();
for (ty_id, ty) in types.types.iter().enumerate() {
if ty.ty.path.namespace().is_empty() {
continue;
};
let groups_with_same_path = types_with_same_type_path_grouped_by_shape
.entry(&ty.ty.path.segments)
.or_default();
let mut added_to_existing_group = false;
for group in groups_with_same_path.iter_mut() {
let ty_id_b = group[0]; let ty_b = types.resolve(ty_id_b).expect("ty exists");
if types_equal_extended_to_params(&ty.ty, ty_b) {
group.push(ty_id_b);
added_to_existing_group = true;
break;
}
}
if !added_to_existing_group {
groups_with_same_path.push(vec![ty_id as u32])
}
}
let groups_that_need_renaming = types_with_same_type_path_grouped_by_shape
.into_values()
.filter(|g| g.len() > 1)
.collect::<Vec<_>>(); for groups_with_same_path in groups_that_need_renaming {
let mut n = 1;
for group_with_same_shape in groups_with_same_path {
for ty_id in group_with_same_shape {
let ty = types
.types
.get_mut(ty_id as usize)
.expect("type is present; qed;");
let name = ty.ty.path.segments.last_mut().expect("This is only empty for builtin types, that are filtered out with namespace().is_empty() above; qed;");
*name = format!("{name}{n}"); }
n += 1;
}
}
}
fn types_equal_extended_to_params(a: &Type<PortableForm>, b: &Type<PortableForm>) -> bool {
let collect_params = |type_params: &[TypeParameter<PortableForm>]| {
type_params
.iter()
.filter_map(|p| p.ty.map(|ty| (ty.id, p.name.clone())))
.collect::<HashMap<u32, String>>()
};
let type_params_a = collect_params(&a.type_params);
let type_params_b = collect_params(&a.type_params);
if type_params_a.len() != type_params_b.len() {
return false;
}
let ids_equal = |a: u32, b: u32| -> bool {
a == b
|| matches!((type_params_a.get(&a), type_params_b.get(&b)), (Some(a_name), Some(b_name)) if a_name == b_name)
};
let fields_equal = |a: &[Field<PortableForm>], b: &[Field<PortableForm>]| -> bool {
if a.len() != b.len() {
return false;
}
a.iter().zip(b.iter()).all(|(a, b)| {
a.name == b.name && a.type_name == b.type_name && ids_equal(a.ty.id, b.ty.id)
})
};
match (&a.type_def, &b.type_def) {
(TypeDef::Composite(a), TypeDef::Composite(b)) => fields_equal(&a.fields, &b.fields),
(TypeDef::Variant(a), TypeDef::Variant(b)) => {
a.variants.len() == b.variants.len()
&& a.variants
.iter()
.zip(b.variants.iter())
.all(|(a, b)| a.name == b.name && fields_equal(&a.fields, &b.fields))
}
(TypeDef::Sequence(a), TypeDef::Sequence(b)) => ids_equal(a.type_param.id, b.type_param.id),
(TypeDef::Array(a), TypeDef::Array(b)) => {
a.len == b.len && ids_equal(a.type_param.id, b.type_param.id)
}
(TypeDef::Tuple(a), TypeDef::Tuple(b)) => {
a.fields.len() == b.fields.len()
&& a.fields
.iter()
.zip(b.fields.iter())
.all(|(a, b)| ids_equal(a.id, b.id))
}
(TypeDef::Primitive(a), TypeDef::Primitive(b)) => a == b,
(TypeDef::Compact(a), TypeDef::Compact(b)) => ids_equal(a.type_param.id, b.type_param.id),
(TypeDef::BitSequence(a), scale_info::TypeDef::BitSequence(b)) => {
ids_equal(a.bit_order_type.id, b.bit_order_type.id)
&& ids_equal(a.bit_store_type.id, b.bit_store_type.id)
}
_ => false,
}
}