diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bee45e3..71fbb664 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.11.2] - 2024-04-05 + +- Fix a bug in the `PortableRegistry::retain()` method where recursive types (types containing themselves) were not properly handled. + ## [2.11.1] - 2024-03-22 - Fix a bug in the `PortableRegistry::retain()` method, where a type's id field was not adjusted to the new position of the type in the retained `Vec`. diff --git a/Cargo.toml b/Cargo.toml index 9e0516b7..ea07623c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,12 +17,15 @@ categories.workspace = true [dependencies] bitvec = { version = "1", default-features = false, features = ["alloc"], optional = true } cfg-if = "1.0" -scale-info-derive = { version = "2.11.1", path = "derive", default-features = false, optional = true } +scale-info-derive = { version = "2.11.2", path = "derive", default-features = false, optional = true } serde = { version = "1", default-features = false, optional = true, features = ["derive", "alloc"] } derive_more = { version = "0.99.1", default-features = false, features = ["from"] } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } schemars = { version = "0.8", optional = true } +[dev-dependencies] +scale-info-derive = { version = "2.11.2", path = "derive" } + [features] default = ["std"] std = [ @@ -57,7 +60,7 @@ members = [ ] [workspace.package] -version = "2.11.1" +version = "2.11.2" authors = [ "Parity Technologies ", "Centrality Developers ", diff --git a/src/portable.rs b/src/portable.rs index 165a7dd6..f482e9eb 100644 --- a/src/portable.rs +++ b/src/portable.rs @@ -89,6 +89,18 @@ impl PortableRegistry { let mut retained_mappings = BTreeMap::new(); let mut new_types = crate::prelude::vec![]; + fn placeholder_type() -> PortableType { + PortableType { + id: u32::MAX, + ty: Type { + type_def: TypeDef::Primitive(TypeDefPrimitive::Bool), + path: Path::default(), + type_params: crate::prelude::vec![], + docs: crate::prelude::vec![], + }, + } + } + fn retain_type( id: u32, types: &mut [PortableType], @@ -100,28 +112,30 @@ impl PortableRegistry { return *id; } - // Zero-allocation default implementation that is used as - // a placeholder and never accessed. - let placeholder = PortableType { - id: 0, - ty: Type { - type_def: TypeDef::Primitive(TypeDefPrimitive::Bool), - path: Path::default(), - type_params: crate::prelude::vec![], - docs: crate::prelude::vec![], - }, - }; - // Take the type out of the registry that we'll be retaining: - let mut ty = mem::replace(&mut types[id as usize], placeholder); + // First, save a spot for this type in our new registry. We do this straight away + // so that we can add the type ID to the retained mappings _before_ recursing into + // it below. This means that if a type contains itself, we'll bail above when we + // see the same type again. + let new_id = new_types.len() as u32; + new_types.push(placeholder_type()); + retained_mappings.insert(id, new_id); - // Make sure any type params are also retained: + // Now, take the actual type we'll be retaining out of the old registry, + // swapping it with a placeholder type to avoid any allocations. Because of + // the above, nothing should ever try to access this placeholder type anyway. + let mut ty = mem::replace(&mut types[id as usize], placeholder_type()); + ty.id = new_id; + + // Now we recursively retain any type parameters in the type we're retaining. + // Update their IDs to point to the new locations of the retained types. for param in ty.ty.type_params.iter_mut() { - let Some(ty) = ¶m.ty else { continue }; - let new_id = retain_type(ty.id, types, new_types, retained_mappings); + let Some(param_ty) = ¶m.ty else { continue }; + let new_id = retain_type(param_ty.id, types, new_types, retained_mappings); param.ty = Some(Into::into(new_id)); } - // make sure any types inside this type are also retained and update the IDs: + // Also recurse into any types inside this type to retain them too. Update their IDs + // to point to the new locations of the retained types. match &mut ty.ty.type_def { TypeDef::Composite(composite) => { for field in composite.fields.iter_mut() { @@ -179,11 +193,9 @@ impl PortableRegistry { } } - // Retain this type, having updated any inner IDs: - let new_id = new_types.len() as u32; - ty.id = new_id; - new_types.push(ty); - retained_mappings.insert(id, new_id); + // Now we've updated the IDs etc of this type, we put it into the new registry + // and override our placeholder type that was saving its space for us. + new_types[new_id as usize] = ty; new_id } @@ -292,304 +304,423 @@ impl PortableRegistryBuilder { #[cfg(test)] mod tests { + use scale::Compact; + use super::*; + use crate::ty::TypeDefPrimitive; use crate::{build::*, prelude::vec, *}; - // Type IDs generated by `build_registry`. - const U32_TY_ID: u32 = 0; - const U64_TY_ID: u32 = 1; - const VEC_U32_TY_ID: u32 = 2; - const ARRAY_U32_TY_ID: u32 = 3; - const TUPLE_TY_ID: u32 = 4; - const COMPACT_TY_ID: u32 = 5; - const BIT_SEQ_TY_ID: u32 = 6; - const COMPOSITE_TY_ID: u32 = 7; - const VARIANT_TY_ID: u32 = 8; - - fn build_registry() -> PortableRegistry { - let mut builder = PortableRegistryBuilder::new(); - // Primitives - let u32_type = Type::new(Path::default(), vec![], TypeDefPrimitive::U32, vec![]); - let u32_type_id = builder.register_type(u32_type); - assert_eq!(U32_TY_ID, u32_type_id); - - let u64_type = Type::new(Path::default(), vec![], TypeDefPrimitive::U64, vec![]); - let u64_type_id = builder.register_type(u64_type); - assert_eq!(U64_TY_ID, u64_type_id); + fn ty() -> MetaType { + MetaType::new::() + } - // Sequence - let vec_u32_type = Type::new( - Path::default(), - vec![], - TypeDefSequence::new(u32_type_id.into()), - vec![], - ); - let vec_u32_type_id = builder.register_type(vec_u32_type); - assert_eq!(VEC_U32_TY_ID, vec_u32_type_id); + fn make_registry(tys: impl IntoIterator) -> (Vec, PortableRegistry) { + // Register our types, recording the corresponding IDs. + let mut types = Registry::new(); + let mut ids = vec![]; + for ty in tys.into_iter() { + let id = types.register_type(&ty); + ids.push(id.id); + } - // Array - let array_u32_type = Type::new( - Path::default(), - vec![], - TypeDefArray::new(3, u32_type_id.into()), - vec![], - ); - let array_u32_type_id = builder.register_type(array_u32_type); - assert_eq!(ARRAY_U32_TY_ID, array_u32_type_id); + let registry = types.into(); + (ids, registry) + } - // Tuple - let tuple_type = Type::new( - Path::default(), - vec![], - TypeDefTuple::new_portable(vec![u32_type_id.into(), u64_type_id.into()]), - vec![], - ); - let tuple_type_id = builder.register_type(tuple_type); - assert_eq!(TUPLE_TY_ID, tuple_type_id); + #[test] + fn retain_seq_type() { + let (ids, mut registry) = make_registry([ty::(), ty::>(), ty::()]); - // Compact - let compact_type = Type::new( - Path::default(), - vec![], - TypeDefCompact::new(tuple_type_id.into()), - vec![], - ); - let compact_type_id = builder.register_type(compact_type); - assert_eq!(COMPACT_TY_ID, compact_type_id); + assert_eq!(registry.types.len(), 4); - // BitSequence - let bit_seq_type = Type::new( - Path::default(), - vec![], - TypeDefBitSequence::new_portable(u32_type_id.into(), u64_type_id.into()), - vec![], - ); - let bit_seq_type_id = builder.register_type(bit_seq_type); - assert_eq!(BIT_SEQ_TY_ID, bit_seq_type_id); + // Retain only the vec. + let vec_id = ids[1]; + let retained_ids = registry.retain(|id| id == vec_id); - // Composite - let composite_type = Type::builder_portable() - .path(Path::from_segments_unchecked(["MyStruct".into()])) - .composite( - Fields::named() - .field_portable(|f| f.name("primitive".into()).ty(u32_type_id)) - .field_portable(|f| f.name("vec_of_u32".into()).ty(vec_u32_type_id)), - ); - let composite_type_id = builder.register_type(composite_type); - assert_eq!(COMPOSITE_TY_ID, composite_type_id); - - // Variant - let enum_type = Type::builder_portable() - .path(Path::from_segments_unchecked(["MyEnum".into()])) - .variant( - Variants::new() - .variant("A".into(), |v| { - v.index(0).fields( - Fields::::named() - .field_portable(|f| f.name("primitive".into()).ty(u32_type_id)) - .field_portable(|f| { - f.name("vec_of_u32".into()).ty(vec_u32_type_id) - }), - ) - }) - .variant_unit("B".into(), 1), - ); - let enum_type_id = builder.register_type(enum_type); - assert_eq!(VARIANT_TY_ID, enum_type_id); + assert_eq!(retained_ids.len(), 2); + assert_eq!(registry.types.len(), 2); - builder.finish() + // Check that vec was retained and has correct ID. + let new_vec_id = *retained_ids + .get(&vec_id) + .expect("vec should have been retained"); + let registry_ty = registry + .types + .get(new_vec_id as usize) + .expect("vec should exist"); + + assert_eq!(registry_ty.id, new_vec_id); + + // Check that vec type info is as expected. + let seq = match ®istry_ty.ty.type_def { + TypeDef::Sequence(s) => s, + def => panic!("Expected a sequence type, got {def:?}"), + }; + + let vec_param = registry + .resolve(seq.type_param.id) + .expect("vec param should be exist"); + assert!(matches!( + vec_param.type_def, + TypeDef::Primitive(TypeDefPrimitive::U32) + )); } #[test] - fn retain_recursive_seq() { - let mut registry = build_registry(); + fn retain_array_type() { + let (ids, mut registry) = make_registry([ty::(), ty::<[u32; 16]>(), ty::()]); - let ids_result = registry.retain(|id| id == VEC_U32_TY_ID); - assert_eq!(ids_result.len(), 2); - assert_eq!(ids_result.get(&U32_TY_ID), Some(&0)); - assert_eq!(ids_result.get(&VEC_U32_TY_ID), Some(&1)); + assert_eq!(registry.types.len(), 4); + + // Retain only the array. + let arr_id = ids[1]; + let retained_ids = registry.retain(|id| id == arr_id); + assert_eq!(retained_ids.len(), 2); assert_eq!(registry.types.len(), 2); - let expected_ty = Type::new(Path::default(), vec![], TypeDefPrimitive::U32, vec![]); - assert_eq!(registry.resolve(0).unwrap(), &expected_ty); - let expected_ty = Type::new( - Path::default(), - vec![], - TypeDefSequence::new(0.into()), - vec![], - ); - assert_eq!(registry.resolve(1).unwrap(), &expected_ty); + + // Check that array was retained and has correct ID. + let new_arr_id = *retained_ids + .get(&arr_id) + .expect("array should have been retained"); + let registry_ty = registry + .types + .get(new_arr_id as usize) + .expect("array should exist"); + + assert_eq!(registry_ty.id, new_arr_id); + + // Check that array type info is as expected. + let arr = match ®istry_ty.ty.type_def { + TypeDef::Array(a) => a, + def => panic!("Expected an array type, got {def:?}"), + }; + + let array_param = registry + .resolve(arr.type_param.id) + .expect("array param should be exist"); + assert!(matches!( + array_param.type_def, + TypeDef::Primitive(TypeDefPrimitive::U32) + )); } #[test] - fn retain_recursive_array() { - let mut registry = build_registry(); + fn retain_tuple_type() { + let (ids, mut registry) = + make_registry([ty::(), ty::<(u32, [u8; 32], bool)>(), ty::()]); - let ids_result = registry.retain(|id| id == ARRAY_U32_TY_ID); - assert_eq!(ids_result.len(), 2); - assert_eq!(ids_result.get(&U32_TY_ID), Some(&0)); - assert_eq!(ids_result.get(&ARRAY_U32_TY_ID), Some(&1)); + assert_eq!(registry.types.len(), 6); - assert_eq!(registry.types.len(), 2); - let expected_ty = Type::new(Path::default(), vec![], TypeDefPrimitive::U32, vec![]); - assert_eq!(registry.resolve(0).unwrap(), &expected_ty); - let expected_ty = Type::new( - Path::default(), - vec![], - TypeDefArray::new(3, 0.into()), - vec![], - ); - assert_eq!(registry.resolve(1).unwrap(), &expected_ty); + // Retain only the tuple. + let tuple_id = ids[1]; + let retained_ids = registry.retain(|id| id == tuple_id); + + // We only actually ditch the String when retaining: + assert_eq!(retained_ids.len(), 5); + assert_eq!(registry.types.len(), 5); + + // Check that tuple was retained and has correct ID. + let new_tuple_id = *retained_ids + .get(&tuple_id) + .expect("tuple should have been retained"); + let registry_ty = registry + .types + .get(new_tuple_id as usize) + .expect("tuple should exist"); + + assert_eq!(registry_ty.id, new_tuple_id); + + // Check that tuple type info is as expected. + let tup = match ®istry_ty.ty.type_def { + TypeDef::Tuple(t) => t, + def => panic!("Expected an tuple type, got {def:?}"), + }; + + // Check that tuple fields are as expected. + assert!(matches!( + registry.resolve(tup.fields[0].id).unwrap().type_def, + TypeDef::Primitive(TypeDefPrimitive::U32) + )); + assert!(matches!( + registry.resolve(tup.fields[1].id).unwrap().type_def, + TypeDef::Array(_) + )); + assert!(matches!( + registry.resolve(tup.fields[2].id).unwrap().type_def, + TypeDef::Primitive(TypeDefPrimitive::Bool) + )); } #[test] - fn retain_recursive_tuple() { - let mut registry = build_registry(); + fn retain_composite_type() { + #[derive(scale_info_derive::TypeInfo)] + #[allow(dead_code)] + struct Foo { + a: u32, + b: [u8; 32], + c: bool, + } - let ids_result = registry.retain(|id| id == TUPLE_TY_ID); - assert_eq!(ids_result.len(), 3); - assert_eq!(ids_result.get(&U32_TY_ID), Some(&0)); - assert_eq!(ids_result.get(&U64_TY_ID), Some(&1)); - assert_eq!(ids_result.get(&TUPLE_TY_ID), Some(&2)); + let (ids, mut registry) = make_registry([ty::(), ty::(), ty::()]); - assert_eq!(registry.types.len(), 3); - let expected_ty = Type::new(Path::default(), vec![], TypeDefPrimitive::U32, vec![]); - assert_eq!(registry.resolve(0).unwrap(), &expected_ty); - let expected_ty = Type::new(Path::default(), vec![], TypeDefPrimitive::U64, vec![]); - assert_eq!(registry.resolve(1).unwrap(), &expected_ty); - let expected_ty = Type::new( - Path::default(), - vec![], - TypeDefTuple::new_portable(vec![0.into(), 1.into()]), - vec![], - ); - assert_eq!(registry.resolve(2).unwrap(), &expected_ty); + assert_eq!(registry.types.len(), 6); + + // Retain only the struct. + let struct_id = ids[1]; + let retained_ids = registry.retain(|id| id == struct_id); + + // We only actually ditch the String when retaining: + assert_eq!(retained_ids.len(), 5); + assert_eq!(registry.types.len(), 5); + + // Check that struct was retained and has correct ID. + let struct_id = *retained_ids + .get(&struct_id) + .expect("struct should have been retained"); + let registry_ty = registry + .types + .get(struct_id as usize) + .expect("struct should exist"); + + assert_eq!(registry_ty.id, struct_id); + + // Check that struct type info is as expected. + let struc = match ®istry_ty.ty.type_def { + TypeDef::Composite(s) => s, + def => panic!("Expected an struct type, got {def:?}"), + }; + + // Check that struct fields are as expected. + assert_eq!(struc.fields.len(), 3); + assert_eq!(struc.fields[0].name, Some("a".to_owned())); + assert!(matches!( + registry.resolve(struc.fields[0].ty.id).unwrap().type_def, + TypeDef::Primitive(TypeDefPrimitive::U32) + )); + assert_eq!(struc.fields[1].name, Some("b".to_owned())); + assert!(matches!( + registry.resolve(struc.fields[1].ty.id).unwrap().type_def, + TypeDef::Array(_) + )); + assert_eq!(struc.fields[2].name, Some("c".to_owned())); + assert!(matches!( + registry.resolve(struc.fields[2].ty.id).unwrap().type_def, + TypeDef::Primitive(TypeDefPrimitive::Bool) + )); } #[test] - fn retain_recursive_compact() { - let mut registry = build_registry(); + fn retain_variant_type() { + #[derive(scale_info_derive::TypeInfo)] + #[allow(dead_code)] + enum Foo { + A(u32), + B(bool), + } - let ids_result = registry.retain(|id| id == COMPACT_TY_ID); - assert_eq!(ids_result.len(), 4); - assert_eq!(ids_result.get(&U32_TY_ID), Some(&0)); - assert_eq!(ids_result.get(&U64_TY_ID), Some(&1)); - assert_eq!(ids_result.get(&TUPLE_TY_ID), Some(&2)); - assert_eq!(ids_result.get(&COMPACT_TY_ID), Some(&3)); + let (ids, mut registry) = make_registry([ty::(), ty::(), ty::()]); assert_eq!(registry.types.len(), 4); - let expected_ty = Type::new(Path::default(), vec![], TypeDefPrimitive::U32, vec![]); - assert_eq!(registry.resolve(0).unwrap(), &expected_ty); - let expected_ty = Type::new(Path::default(), vec![], TypeDefPrimitive::U64, vec![]); - assert_eq!(registry.resolve(1).unwrap(), &expected_ty); - let expected_ty = Type::new( - Path::default(), - vec![], - TypeDefTuple::new_portable(vec![0.into(), 1.into()]), - vec![], - ); - assert_eq!(registry.resolve(2).unwrap(), &expected_ty); - let expected_ty = Type::new( - Path::default(), - vec![], - TypeDefCompact::new(2.into()), - vec![], - ); - assert_eq!(registry.resolve(3).unwrap(), &expected_ty); + + // Retain only the variant. + let variant_id = ids[1]; + let retained_ids = registry.retain(|id| id == variant_id); + + // We only actually ditch the String when retaining: + assert_eq!(retained_ids.len(), 3); + assert_eq!(registry.types.len(), 3); + + // Check that variant was retained and has correct ID. + let variant_id = *retained_ids + .get(&variant_id) + .expect("variant should have been retained"); + let registry_ty = registry + .types + .get(variant_id as usize) + .expect("variant should exist"); + + assert_eq!(registry_ty.id, variant_id); + + // Check that variant type info is as expected. + let var = match ®istry_ty.ty.type_def { + TypeDef::Variant(v) => v, + def => panic!("Expected a variant type, got {def:?}"), + }; + + assert_eq!(var.variants.len(), 2); + assert_eq!(var.variants[0].name, "A".to_owned()); + assert_eq!(var.variants[0].fields.len(), 1); + assert!(matches!( + registry + .resolve(var.variants[0].fields[0].ty.id) + .unwrap() + .type_def, + TypeDef::Primitive(TypeDefPrimitive::U32) + )); + + assert_eq!(var.variants[1].name, "B".to_owned()); + assert_eq!(var.variants[1].fields.len(), 1); + assert!(matches!( + registry + .resolve(var.variants[1].fields[0].ty.id) + .unwrap() + .type_def, + TypeDef::Primitive(TypeDefPrimitive::Bool) + )); } #[test] - fn retain_recursive_bit_seq() { - let mut registry = build_registry(); + fn retain_compact_type() { + let (ids, mut registry) = + make_registry([ty::(), ty::(), ty::>()]); - let ids_result = registry.retain(|id| id == BIT_SEQ_TY_ID); - assert_eq!(ids_result.len(), 3); - assert_eq!(ids_result.get(&U32_TY_ID), Some(&0)); - assert_eq!(ids_result.get(&U64_TY_ID), Some(&1)); - assert_eq!(ids_result.get(&BIT_SEQ_TY_ID), Some(&2)); + assert_eq!(registry.types.len(), 4); - assert_eq!(registry.types.len(), 3); - let expected_ty = Type::new(Path::default(), vec![], TypeDefPrimitive::U32, vec![]); - assert_eq!(registry.resolve(0).unwrap(), &expected_ty); - let expected_ty = Type::new(Path::default(), vec![], TypeDefPrimitive::U64, vec![]); - assert_eq!(registry.resolve(1).unwrap(), &expected_ty); - let expected_ty = Type::new( - Path::default(), - vec![], - TypeDefBitSequence::new_portable(0.into(), 1.into()), - vec![], - ); - assert_eq!(registry.resolve(2).unwrap(), &expected_ty); + // Retain only the compact. + let compact_id = ids[2]; + let retained_ids = registry.retain(|id| id == compact_id); + + assert_eq!(retained_ids.len(), 2); + assert_eq!(registry.types.len(), 2); + + // Check that compact was retained and has correct ID. + let compact_id = *retained_ids + .get(&compact_id) + .expect("compact should have been retained"); + let registry_ty = registry + .types + .get(compact_id as usize) + .expect("compact should exist"); + + assert_eq!(registry_ty.id, compact_id); + + // Check that compact type info is as expected. + let compact = match ®istry_ty.ty.type_def { + TypeDef::Compact(c) => c, + def => panic!("Expected a compact type, got {def:?}"), + }; + + // And the compact param should be a u32. + assert!(matches!( + registry.resolve(compact.type_param.id).unwrap().type_def, + TypeDef::Primitive(TypeDefPrimitive::U32) + )); } #[test] - fn retain_recursive_composite() { - let mut registry = build_registry(); + fn retain_bitsequence_type() { + // Use a more verbose method to build the registry to avoid + // needing to pull in BitVec as a dev dep: + let mut builder = PortableRegistryBuilder::new(); - let ids_result = registry.retain(|id| id == COMPOSITE_TY_ID); - assert_eq!(ids_result.len(), 3); - assert_eq!(ids_result.get(&U32_TY_ID), Some(&0)); - assert_eq!(ids_result.get(&VEC_U32_TY_ID), Some(&1)); - assert_eq!(ids_result.get(&COMPOSITE_TY_ID), Some(&2)); + // Register a couple of primitives: + let bool_type = Type::new(Path::default(), vec![], TypeDefPrimitive::Bool, vec![]); + let _bool_type_id = builder.register_type(bool_type); - assert_eq!(registry.types.len(), 3); - let expected_ty = Type::new(Path::default(), vec![], TypeDefPrimitive::U32, vec![]); - assert_eq!(registry.resolve(0).unwrap(), &expected_ty); - let expected_ty = Type::new( + let u32_type = Type::new(Path::default(), vec![], TypeDefPrimitive::U32, vec![]); + let u32_type_id = builder.register_type(u32_type); + + let u64_type = Type::new(Path::default(), vec![], TypeDefPrimitive::U64, vec![]); + let u64_type_id = builder.register_type(u64_type); + + // Register a bit sequence: + let bit_seq_type = Type::new( Path::default(), vec![], - TypeDefSequence::new(0.into()), + TypeDefBitSequence::new_portable(u32_type_id.into(), u64_type_id.into()), vec![], ); - assert_eq!(registry.resolve(1).unwrap(), &expected_ty); - let expected_ty = Type::builder_portable() - .path(Path::from_segments_unchecked(["MyStruct".into()])) - .composite( - Fields::named() - .field_portable(|f| f.name("primitive".into()).ty(0)) - .field_portable(|f| f.name("vec_of_u32".into()).ty(1)), - ); - assert_eq!(registry.resolve(2).unwrap(), &expected_ty); + let bit_seq_type_id = builder.register_type(bit_seq_type); - for (idx, ty) in registry.types.iter().enumerate() { - assert_eq!(idx, ty.id as usize); - } + // Now we have a registry with the above types in: + let mut registry = builder.finish(); + + assert_eq!(registry.types.len(), 4); + + // Retain only the bitseq. + let retained_ids = registry.retain(|id| id == bit_seq_type_id); + + // One bitsequence entry + 2 params for it: + assert_eq!(retained_ids.len(), 3); + assert_eq!(registry.types.len(), 3); + + // Check that bitseq was retained and has correct ID. + let bitseq_id = *retained_ids + .get(&bit_seq_type_id) + .expect("bitseq should have been retained"); + let registry_ty = registry + .types + .get(bitseq_id as usize) + .expect("bitseq should exist"); + + assert_eq!(registry_ty.id, bitseq_id); + + // Check that bitseq type info is as expected. + let bitseq = match ®istry_ty.ty.type_def { + TypeDef::BitSequence(b) => b, + def => panic!("Expected a bit sequence type, got {def:?}"), + }; + assert!(matches!( + registry.resolve(bitseq.bit_store_type.id).unwrap().type_def, + TypeDef::Primitive(TypeDefPrimitive::U32) + )); + assert!(matches!( + registry.resolve(bitseq.bit_order_type.id).unwrap().type_def, + TypeDef::Primitive(TypeDefPrimitive::U64) + )); } #[test] - fn retain_recursive_variant() { - let mut registry = build_registry(); + fn retain_recursive_type() { + #[derive(scale_info_derive::TypeInfo)] + #[allow(dead_code)] + enum Recursive { + Value(Box), + Empty, + } - let ids_result = registry.retain(|id| id == VARIANT_TY_ID); - assert_eq!(ids_result.len(), 3); - assert_eq!(ids_result.get(&U32_TY_ID), Some(&0)); - assert_eq!(ids_result.get(&VEC_U32_TY_ID), Some(&1)); - assert_eq!(ids_result.get(&VARIANT_TY_ID), Some(&2)); + let (ids, mut registry) = make_registry([ty::(), ty::(), ty::()]); assert_eq!(registry.types.len(), 3); - let expected_ty = Type::new(Path::default(), vec![], TypeDefPrimitive::U32, vec![]); - assert_eq!(registry.resolve(0).unwrap(), &expected_ty); - let expected_ty = Type::new( - Path::default(), - vec![], - TypeDefSequence::new(0.into()), - vec![], - ); - assert_eq!(registry.resolve(1).unwrap(), &expected_ty); - let expected_ty = Type::builder_portable() - .path(Path::from_segments_unchecked(["MyEnum".into()])) - .variant( - Variants::new() - .variant("A".into(), |v| { - v.index(0).fields( - Fields::::named() - .field_portable(|f| f.name("primitive".into()).ty(0)) - .field_portable(|f| f.name("vec_of_u32".into()).ty(1)), - ) - }) - .variant_unit("B".into(), 1), - ); - assert_eq!(registry.resolve(2).unwrap(), &expected_ty); + + // Retain only the recursive type. + let variant_id = ids[1]; + let retained_ids = registry.retain(|id| id == variant_id); + + assert_eq!(retained_ids.len(), 1); + assert_eq!(registry.types.len(), 1); + + // Check that variant was retained and has correct ID. + let variant_id = *retained_ids + .get(&variant_id) + .expect("variant should have been retained"); + let registry_ty = registry + .types + .get(variant_id as usize) + .expect("variant should exist"); + + assert_eq!(registry_ty.id, variant_id); + + // Check that variant type info is as expected. + let var = match ®istry_ty.ty.type_def { + TypeDef::Variant(v) => v, + def => panic!("Expected a variant type, got {def:?}"), + }; + + assert_eq!(var.variants.len(), 2); + assert_eq!(var.variants[0].name, "Value".to_owned()); + assert_eq!(var.variants[0].fields.len(), 1); + assert!(matches!( + registry + .resolve(var.variants[0].fields[0].ty.id) + .unwrap() + .type_def, + TypeDef::Variant(_) + )); + + assert_eq!(var.variants[1].name, "Empty".to_owned()); + assert_eq!(var.variants[1].fields.len(), 0); } #[test] @@ -649,101 +780,4 @@ mod tests { assert_eq!(Some(&vec_u32_type), registry.resolve(vec_u32_type_id)); assert_eq!(Some(&composite_type), registry.resolve(composite_type_id)); } - - #[test] - fn retain_ids() { - let mut builder = PortableRegistryBuilder::new(); - let u32_type = Type::new(Path::default(), vec![], TypeDefPrimitive::U32, vec![]); - let _u32_type_id = builder.register_type(u32_type); - - let u64_type = Type::new(Path::default(), vec![], TypeDefPrimitive::U64, vec![]); - let u64_type_id = builder.register_type(u64_type.clone()); - - let mut registry = builder.finish(); - assert_eq!(registry.types.len(), 2); - - let ids_result = registry.retain(|id| id == u64_type_id); - assert_eq!(ids_result.len(), 1); - assert_eq!(ids_result.get(&u64_type_id), Some(&0)); - - assert_eq!(registry.types.len(), 1); - assert_eq!(registry.resolve(0).unwrap(), &u64_type); - } - - #[test] - fn retain_recursive_ids() { - let mut builder = PortableRegistryBuilder::new(); - let u32_type = Type::new(Path::default(), vec![], TypeDefPrimitive::U32, vec![]); - let u32_type_id = builder.register_type(u32_type.clone()); - - let u64_type = Type::new(Path::default(), vec![], TypeDefPrimitive::U64, vec![]); - let _u64_type_id = builder.register_type(u64_type); - - let vec_u32_type = Type::new( - Path::default(), - vec![], - TypeDefSequence::new(u32_type_id.into()), - vec![], - ); - let vec_u32_type_id = builder.register_type(vec_u32_type); - - let composite_type = Type::builder_portable() - .path(Path::from_segments_unchecked(["MyStruct".into()])) - .composite( - Fields::named() - .field_portable(|f| f.name("primitive".into()).ty(u32_type_id)) - .field_portable(|f| f.name("vec_of_u32".into()).ty(vec_u32_type_id)), - ); - let composite_type_id = builder.register_type(composite_type); - - let composite_type_second = Type::builder_portable() - .path(Path::from_segments_unchecked(["MyStructSecond".into()])) - .composite( - Fields::named() - .field_portable(|f| f.name("vec_of_u32".into()).ty(vec_u32_type_id)) - .field_portable(|f| f.name("second".into()).ty(composite_type_id)), - ); - let composite_type_second_id = builder.register_type(composite_type_second); - - let mut registry = builder.finish(); - assert_eq!(registry.types.len(), 5); - - let ids_result = registry.retain(|id| id == composite_type_second_id); - assert_eq!(ids_result.len(), 4); - assert_eq!(ids_result.get(&u32_type_id), Some(&0)); - assert_eq!(ids_result.get(&vec_u32_type_id), Some(&1)); - assert_eq!(ids_result.get(&composite_type_id), Some(&2)); - assert_eq!(ids_result.get(&composite_type_second_id), Some(&3)); - - assert_eq!(registry.types.len(), 4); - - // New type IDs are generated in DFS manner. - assert_eq!(registry.resolve(0).unwrap(), &u32_type); - - let expected_type = Type::new( - Path::default(), - vec![], - TypeDefSequence::new(0.into()), - vec![], - ); - assert_eq!(registry.resolve(1).unwrap(), &expected_type); - - let expected_type = Type::builder_portable() - .path(Path::from_segments_unchecked(["MyStruct".into()])) - .composite( - Fields::named() - .field_portable(|f| f.name("primitive".into()).ty(0)) - .field_portable(|f| f.name("vec_of_u32".into()).ty(1)), - ); - assert_eq!(registry.resolve(2).unwrap(), &expected_type); - - let expected_type = Type::builder_portable() - .path(Path::from_segments_unchecked(["MyStructSecond".into()])) - .composite( - Fields::named() - .field_portable(|f| f.name("vec_of_u32".into()).ty(1)) - .field_portable(|f| f.name("second".into()).ty(2)), - ); - assert_eq!(registry.resolve(3).unwrap(), &expected_type); - } }