From ffa5ea39c930287d6c14870541f3673a473fc756 Mon Sep 17 00:00:00 2001 From: Tadeo hepperle Date: Mon, 27 Nov 2023 18:33:05 +0100 Subject: [PATCH] add documentation to everything. --- Cargo.toml | 5 +++- description/Cargo.toml | 14 +++++----- description/src/formatting.rs | 1 + description/src/lib.rs | 8 ++++++ description/src/type_example/mod.rs | 2 ++ description/src/type_example/rust_value.rs | 5 ++++ description/src/type_example/scale_value.rs | 2 ++ typegen/src/lib.rs | 5 ++++ typegen/src/typegen/error.rs | 11 ++++++++ typegen/src/typegen/ir/mod.rs | 2 ++ typegen/src/typegen/ir/module_ir.rs | 6 ++++ typegen/src/typegen/ir/type_ir.rs | 30 +++++++++++++++++++- typegen/src/typegen/mod.rs | 13 +++++++++ typegen/src/typegen/resolve_type_paths.rs | 2 ++ typegen/src/typegen/settings/mod.rs | 12 ++++++++ typegen/src/typegen/settings/substitutes.rs | 4 +++ typegen/src/typegen/type_params.rs | 3 +- typegen/src/typegen/type_path.rs | 31 +++++++++++++++++++++ typegen/src/utils.rs | 2 ++ 19 files changed, 147 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2dec50b..f6344ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,4 +34,7 @@ frame-metadata = { version = "16.0.0", default-features = false, features = ["cu bitvec = { version = "1", default-features = false } pretty_assertions = "1.4.0" anyhow = "1.0.75" -# crates +peekmore = "1.3.0" +scale-value = { version = "0.13.0" } +rand_chacha = { version = "0.3.1" } +rand = { version = "0.8.5" } diff --git a/description/Cargo.toml b/description/Cargo.toml index 7fbad07..e061f58 100644 --- a/description/Cargo.toml +++ b/description/Cargo.toml @@ -14,18 +14,18 @@ type-example = ["dep:scale-value", "dep:proc-macro2", "dep:rand_chacha", "dep:ra [dependencies] anyhow = { workspace = true } -peekmore = "1.3.0" +peekmore = { workspace = true } smallvec = { workspace = true } scale-info = { workspace = true } - -scale-value = { version = "0.13.0", optional = true } -proc-macro2 = { version = "1.0.69", optional = true } -rand_chacha = { version = "0.3.1", optional = true } -rand = { version = "0.8.5", optional = true } - scale-typegen = { workspace = true } quote = { workspace = true } +#dependencies for "type-example" feature: +scale-value = { workspace = true, optional = true } +proc-macro2 = { workspace = true, optional = true } +rand_chacha = { workspace = true, optional = true } +rand = { workspace = true, optional = true } + [dev-dependencies] indoc = "2" pretty_assertions = { workspace = true } diff --git a/description/src/formatting.rs b/description/src/formatting.rs index f4a1123..43080aa 100644 --- a/description/src/formatting.rs +++ b/description/src/formatting.rs @@ -3,6 +3,7 @@ use std::str::Chars; use peekmore::{PeekMore, PeekMoreIterator}; use smallvec::SmallVec; +/// Formats a type description string to have nice indents. pub fn format_type_description(input: &str) -> String { /// Big scope means we want to spread out items over multiple lines. /// Small scope means, we want to keep it compact (on one line). diff --git a/description/src/lib.rs b/description/src/lib.rs index 3bc3c5a..5a1252e 100644 --- a/description/src/lib.rs +++ b/description/src/lib.rs @@ -12,12 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! A crate for turning a type from a [`scale_info::PortableRegistry`] into some other, fully resolved, tree-like representation. +//! Currently we can generate these representations for a type: +//! - A human readable description of the type via [`crate::type_description`]. +//! - An exemplary rust value of the type via [`crate::rust_value`]. +//! - An exemplary scale value of the type via [`crate::scale_value`]. + #![deny(unused_crate_dependencies)] +#![deny(missing_docs)] mod description; mod formatting; mod transformer; +/// Create type examples for a type registry. #[cfg(feature = "type-example")] pub mod type_example; diff --git a/description/src/type_example/mod.rs b/description/src/type_example/mod.rs index 404bb34..8e7709a 100644 --- a/description/src/type_example/mod.rs +++ b/description/src/type_example/mod.rs @@ -1,4 +1,6 @@ +/// Generate an exemplary rust value of some type pub mod rust_value; +/// Generate an exemplary scale value of some type pub mod scale_value; #[cfg(test)] diff --git a/description/src/type_example/rust_value.rs b/description/src/type_example/rust_value.rs index 018d264..71bb3e9 100644 --- a/description/src/type_example/rust_value.rs +++ b/description/src/type_example/rust_value.rs @@ -100,6 +100,7 @@ impl<'a> CodeTransformer<'a> { } } +/// Generates a random rust value for a type from the registry. The result should be a valid rust expression. pub fn example( type_id: u32, types: &PortableRegistry, @@ -108,6 +109,10 @@ pub fn example( example_from_seed(type_id, types, settings_for_path_resolver, 42, None, None) } +/// Generates a random rust value for a type from the registry. The result should be a valid rust expression. You can specify a seed to get reproducable results. +/// The `ty_middleware` can be used, to return a different type when a certain type is encountered. +/// The `ty_path_middleware` can be used, to convert an type path encountered into a different type path. +/// E.g. turning `::std::vec::Vec` into just `Vec`. pub fn example_from_seed( type_id: u32, types: &PortableRegistry, diff --git a/description/src/type_example/scale_value.rs b/description/src/type_example/scale_value.rs index 2f3a2ac..54e7ff5 100644 --- a/description/src/type_example/scale_value.rs +++ b/description/src/type_example/scale_value.rs @@ -11,11 +11,13 @@ use crate::transformer::Transformer; type ValueTransformer<'a> = Transformer<'a, Value, RefCell>; +/// Generates a random scale value for a type from the registry. pub fn example(id: u32, types: &PortableRegistry) -> anyhow::Result { const MAGIC_SEED: u64 = 42; example_from_seed(id, types, MAGIC_SEED) } +/// Generates a random scale value for a type from the registry. You can specify the seed to get reproducable results. pub fn example_from_seed(id: u32, types: &PortableRegistry, seed: u64) -> anyhow::Result { fn error_on_recurse( _type_id: u32, diff --git a/typegen/src/lib.rs b/typegen/src/lib.rs index 325686b..fccb7bd 100644 --- a/typegen/src/lib.rs +++ b/typegen/src/lib.rs @@ -12,8 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! A library based on [scale-info](https://github.com/paritytech/scale-info) to transpile portable registries of types into rust type definitions. #![deny(unused_crate_dependencies)] +#![deny(missing_docs)] +// The #![deny(unused_crate_dependencies)] requires us to do these for the example to work: #[cfg(test)] use frame_metadata as _; #[cfg(test)] @@ -21,7 +24,9 @@ use prettyplease as _; #[cfg(test)] use scale_bits as _; +/// Type Generation Settings and Logic pub mod typegen; +/// Utilities for handling Type Registries pub mod utils; pub use typegen::{ diff --git a/typegen/src/typegen/error.rs b/typegen/src/typegen/error.rs index 415c938..4e90720 100644 --- a/typegen/src/typegen/error.rs +++ b/typegen/src/typegen/error.rs @@ -1,20 +1,28 @@ use proc_macro2::Span; +/// Error for when something went wrong during type generation. #[derive(Debug, thiserror::Error)] #[non_exhaustive] pub enum TypegenError { + /// Could not parse into a syn type. #[error("Could not parse into a syn type: {0}")] SynParseError(#[from] syn::Error), + /// Fields should either be all named or all unnamed, make sure you are providing a valid metadata. #[error("Fields should either be all named or all unnamed, make sure you are providing a valid metadata: {0}")] InvalidFields(String), + /// A type in the metadata was invalid #[error("A type in the metadata was invalid: {0}")] InvalidType(String), + /// Could not generate a type that contains a compact type, because the Compact type path is not set in the settings. #[error("Could not generate a type that contains a compact type, because the Compact type path is not set in the settings.")] CompactPathNone, + /// Could not generate a type that contains a bit sequence, because the DecodedBits type path is not set in the settings. #[error("Could not generate a type that contains a bit sequence, because the DecodedBits type path is not set in the settings.")] DecodedBitsPathNone, + /// Could not find type with ID in the type registry. #[error("Could not find type with ID {0} in the type registry.")] TypeNotFound(u32), + /// Type substitution error. #[error("Type substitution error: {0}")] InvalidSubstitute(#[from] TypeSubstitutionError), } @@ -22,7 +30,9 @@ pub enum TypegenError { /// Error attempting to do type substitution. #[derive(Debug, thiserror::Error)] pub struct TypeSubstitutionError { + /// Where in the code the error occured. pub span: Span, + /// Kind of TypeSubstitutionError that happended. pub kind: TypeSubstitutionErrorKind, } @@ -35,6 +45,7 @@ impl std::fmt::Display for TypeSubstitutionError { } } +/// Error attempting to do type substitution. #[derive(Debug, thiserror::Error)] #[non_exhaustive] pub enum TypeSubstitutionErrorKind { diff --git a/typegen/src/typegen/ir/mod.rs b/typegen/src/typegen/ir/mod.rs index edbc95d..8a9a410 100644 --- a/typegen/src/typegen/ir/mod.rs +++ b/typegen/src/typegen/ir/mod.rs @@ -1,2 +1,4 @@ +/// Intermediate Representation of a rust module. pub mod module_ir; +/// Intermediate Representation of a rust type. pub mod type_ir; diff --git a/typegen/src/typegen/ir/module_ir.rs b/typegen/src/typegen/ir/module_ir.rs index ab56c43..f8ce59b 100644 --- a/typegen/src/typegen/ir/module_ir.rs +++ b/typegen/src/typegen/ir/module_ir.rs @@ -10,9 +10,13 @@ use scale_info::form::PortableForm; /// Represents a Rust `mod`, containing generated types and child `mod`s. #[derive(Debug, Clone)] pub struct ModuleIR { + /// Name of this module. pub name: Ident, + /// Root module identifier. pub root_mod: Ident, + /// Submodules of this module. pub children: BTreeMap, + /// Types in this module. pub types: BTreeMap, TypeIR>, } @@ -65,6 +69,8 @@ impl ModuleIR { &self.root_mod } + /// Recursively creates submodules for the given namespace and returns a mutable reference to the innermost module created this way. + /// Returns itself, if the namespace is empty. pub fn get_or_insert_submodule(&mut self, namespace: &[String]) -> &mut ModuleIR { if namespace.is_empty() { return self; diff --git a/typegen/src/typegen/ir/type_ir.rs b/typegen/src/typegen/ir/type_ir.rs index 40c7d43..a01b781 100644 --- a/typegen/src/typegen/ir/type_ir.rs +++ b/typegen/src/typegen/ir/type_ir.rs @@ -5,17 +5,26 @@ use crate::typegen::{ settings::derives::Derives, type_params::TypeParameters, type_path::TypePath, }; +/// Intermediate Representation of a Rust type. #[derive(Debug, Clone)] pub struct TypeIR { + /// Generic type parameters. pub type_params: TypeParameters, + /// Derived traits for his type. pub derives: Derives, + /// whether or not `#[codec(...)]` attributes should be inserted. + /// Only makes sense if the derives include `Encode`/`Decode`. pub insert_codec_attributes: bool, + /// Is this type an enum or struct. pub kind: TypeIRKind, } +/// An enum or struct. #[derive(Debug, Clone)] pub enum TypeIRKind { + /// A struct. Struct(CompositeIR), + /// An enum. Enum(EnumIR), } @@ -35,34 +44,46 @@ impl TypeIR { } } +/// A composite. Could be a struct or a variant of an enum. #[derive(Debug, Clone)] pub struct CompositeIR { + /// Struct name or enum variant name. pub name: Ident, + /// Named, Unnamed or NoFields. pub kind: CompositeIRKind, + /// Docs for the composite. pub docs: TokenStream, } impl CompositeIR { + /// Creates a new `CompositeIR`. pub fn new(name: Ident, kind: CompositeIRKind, docs: TokenStream) -> Self { Self { name, kind, docs } } } +/// A rust enum. #[derive(Debug, Clone)] pub struct EnumIR { + /// Docs for the enum. pub(crate) docs: TokenStream, pub(crate) name: Ident, pub(crate) variants: Vec<(u8, CompositeIR)>, } +/// Named, Unnamed or NoFields. #[derive(Debug, Clone)] pub enum CompositeIRKind { + /// A zero-sized, empty composite. NoFields, + /// Composite with named fields, e.g. a struct. Named(Vec<(Ident, CompositeFieldIR)>), + /// Composite with unnamed fields, e.g. a tuple. Unnamed(Vec), } impl CompositeIRKind { + /// Returns true if this composite be compact encoded. This is only true if the composite has exactly one field which could be compact encoded. pub fn could_derive_as_compact(&self) -> bool { // has to have only a single field: let single_field = match self { @@ -84,14 +105,20 @@ impl CompositeIRKind { } } +/// A field of a composite. #[derive(Debug, Clone)] pub struct CompositeFieldIR { + /// type path of the field. pub type_path: TypePath, + /// Is this field compact encoded? + /// Having this as `true` may insert a `#[codec(compact)]` attribute during code generation. pub is_compact: bool, + /// Is this field actually boxed? e.g. `Box` instead of just `type_path`. pub is_boxed: bool, } impl CompositeFieldIR { + /// Creates a new [`CompositeFieldIR`]. pub fn new(type_path: TypePath, is_compact: bool, is_boxed: bool) -> Self { CompositeFieldIR { type_path, @@ -100,7 +127,8 @@ impl CompositeFieldIR { } } - pub fn compact_attr(&self) -> Option { + /// Returns a `#[codec(compact)]` attribute if the field should be compact encoded. + fn compact_attr(&self) -> Option { self.is_compact.then(|| quote!( #[codec(compact)] )) } } diff --git a/typegen/src/typegen/mod.rs b/typegen/src/typegen/mod.rs index a3c90e6..801cad2 100644 --- a/typegen/src/typegen/mod.rs +++ b/typegen/src/typegen/mod.rs @@ -13,11 +13,17 @@ use quote::quote; use scale_info::{form::PortableForm, PortableRegistry, Type, TypeDef}; use syn::parse_quote; +/// Custom error types. pub mod error; +/// Intermediate representation of types and modules. pub mod ir; +/// Utility extension functions on the `TypeGenerator` struct to resolve type paths. pub mod resolve_type_paths; +/// Settings passed into the `TypeGenerator`. pub mod settings; +/// Logic for dealing with used and unused generic type parameters. pub mod type_params; +/// Type path definition and conversion into tokens. pub mod type_path; /// An interface for generating a types module. @@ -35,10 +41,12 @@ impl<'a> TypeGenerator<'a> { } } + /// The name of the generated module which will contain the generated types. pub fn types_mod_ident(&self) -> &Ident { &self.settings.types_mod_ident } + /// The settings used by this type generator. pub fn settings(&self) -> &TypeGeneratorSettings { self.settings } @@ -81,6 +89,7 @@ impl<'a> TypeGenerator<'a> { Ok(root_mod) } + /// Creates an intermediate representation of a type that can later be converted into rust tokens. pub fn create_type_ir( &self, ty: &Type, @@ -156,6 +165,7 @@ impl<'a> TypeGenerator<'a> { .unwrap_or_default() } + /// Creates an intermediate representation of a composite. pub fn create_composite_ir_kind( &self, fields: &[scale_info::Field], @@ -229,6 +239,8 @@ impl<'a> TypeGenerator<'a> { } } + /// Creates the intermediate representation of a type from just a composite definition. + /// This uses just the default derives and type params are left empty. pub fn upcast_composite(&self, composite: &CompositeIR) -> TypeIR { // just use Default Derives + AsCompact. No access to type specific derives here. (Mainly used in subxt to create structs from enum variants...) let mut derives = self.settings.derives.default_derives().clone(); @@ -243,6 +255,7 @@ impl<'a> TypeGenerator<'a> { } } + /// The default derives set in the type generator's settings. pub fn default_derives(&self) -> &Derives { self.settings.derives.default_derives() } diff --git a/typegen/src/typegen/resolve_type_paths.rs b/typegen/src/typegen/resolve_type_paths.rs index b43019d..b306823 100644 --- a/typegen/src/typegen/resolve_type_paths.rs +++ b/typegen/src/typegen/resolve_type_paths.rs @@ -173,6 +173,7 @@ impl<'a> TypeGenerator<'a> { Ok(TypePath::from_type(ty)) } + /// Converts a [`scale_info::Path`] into a [`TypePathType`], replacing all types that should be substituted. pub fn type_path_maybe_with_substitutes( &self, path: &scale_info::Path, @@ -189,6 +190,7 @@ impl<'a> TypeGenerator<'a> { } } + /// Resolves a type, given some type id. pub fn resolve_type(&self, id: u32) -> Result<&Type, TypegenError> { let ty = self .type_registry diff --git a/typegen/src/typegen/settings/mod.rs b/typegen/src/typegen/settings/mod.rs index 93f4f7c..65457cb 100644 --- a/typegen/src/typegen/settings/mod.rs +++ b/typegen/src/typegen/settings/mod.rs @@ -5,9 +5,12 @@ use syn::parse_quote; use self::substitutes::absolute_path; +/// Settings for which derives should be applied on types pub mod derives; +/// Settings for which types should be substituted by other types. pub mod substitutes; +/// A struct containing all the settings for generating rust types from a type registry. pub struct TypeGeneratorSettings { /// The name of the module which will contain the generated types. pub types_mod_ident: Ident, @@ -51,16 +54,19 @@ impl Default for TypeGeneratorSettings { } impl TypeGeneratorSettings { + /// Creates a new `TypeGeneratorSettings`. pub fn new() -> Self { Self::default() } + /// Sets the `type_mod_name` field. pub fn type_mod_name(mut self, type_mod_name: &str) -> Self { self.types_mod_ident = syn::parse_str(type_mod_name).expect("The provided type_mod_name is not a valid ident"); self } + /// Adds a rule, that a type with path `from` should be replaced with the path `to`. pub fn substitute(mut self, from: syn::Path, to: syn::Path) -> Self { self.substitutes .insert(from, absolute_path(to).unwrap()) @@ -68,31 +74,37 @@ impl TypeGeneratorSettings { self } + /// Sets the `compact_as_type_path` field. pub fn compact_as_type_path(mut self, path: syn::Path) -> Self { self.compact_as_type_path = Some(path); self } + /// Sets the `compact_type_path` field. pub fn compact_type_path(mut self, path: syn::Path) -> Self { self.compact_type_path = Some(path); self } + /// Sets the `decoded_bits_type_path` field. pub fn decoded_bits_type_path(mut self, path: syn::Path) -> Self { self.decoded_bits_type_path = Some(path); self } + /// Sets the `should_gen_docs` field. pub fn should_gen_docs(mut self, should_gen_docs: bool) -> Self { self.should_gen_docs = should_gen_docs; self } + /// Sets the `insert_codec_attributes` field. pub fn insert_codec_attributes(mut self) -> Self { self.insert_codec_attributes = true; self } + /// Adds some derives for all types. pub fn derive_on_all(mut self, derive_paths: impl IntoIterator) -> Self { self.derives.extend_for_all(derive_paths, []); self diff --git a/typegen/src/typegen/settings/substitutes.rs b/typegen/src/typegen/settings/substitutes.rs index 8bdfc1d..b1eefe0 100644 --- a/typegen/src/typegen/settings/substitutes.rs +++ b/typegen/src/typegen/settings/substitutes.rs @@ -20,6 +20,7 @@ pub struct TypeSubstitutes { substitutes: HashMap, } +/// A type that substitutes another type. #[derive(Debug)] pub struct Substitute { path: syn::Path, @@ -239,6 +240,7 @@ impl TypeSubstitutes { } } +/// utility for constructing a `PathSegments` struct. #[macro_export] macro_rules! path_segments { ($($ident: ident)::*) => { @@ -370,10 +372,12 @@ fn is_absolute(path: &syn::Path) -> bool { .map_or(false, |segment| segment.ident == "crate") } +/// tries to convert a [`syn::Path`] into an `AbsolutePath`. Only succeeds if the path is not a relative path. pub fn absolute_path(path: syn::Path) -> Result { path.try_into() } +/// New-type wrapper around [`syn::Path`] pub struct AbsolutePath(syn::Path); impl TryFrom for AbsolutePath { diff --git a/typegen/src/typegen/type_params.rs b/typegen/src/typegen/type_params.rs index 36987f9..ff1ad77 100644 --- a/typegen/src/typegen/type_params.rs +++ b/typegen/src/typegen/type_params.rs @@ -68,8 +68,7 @@ impl TypeParameters { pub fn has_unused_type_params(&self) -> bool { !self.unused.is_empty() } - - pub fn mark_used(&mut self, param: &TypeParameter) { + pub(super) fn mark_used(&mut self, param: &TypeParameter) { self.unused.remove(param); } } diff --git a/typegen/src/typegen/type_path.rs b/typegen/src/typegen/type_path.rs index 5778596..eb720ca 100644 --- a/typegen/src/typegen/type_path.rs +++ b/typegen/src/typegen/type_path.rs @@ -14,9 +14,12 @@ use syn::parse_quote; #[derive(Clone, Debug)] pub struct TypePath(TypePathInner); +/// The type path to either a concrete type or a generic type parameter #[derive(Clone, Debug)] pub enum TypePathInner { + /// Generic type parameter Parameter(TypeParameter), + /// Concrete type Type(TypePathType), } @@ -55,14 +58,17 @@ impl TypePath { } } + /// Returns true, if this is a concrete compact type. pub fn is_compact(&self) -> bool { matches!(&self.0, TypePathInner::Type(ty) if ty.is_compact()) } + /// Returns true, if this is a concrete string type. pub fn is_string(&self) -> bool { matches!(&self.0, TypePathInner::Type(ty) if ty.is_string()) } + /// Returns true, if this is an unsigned integer (anywhere between u8 and u128). pub fn is_uint_up_to_u128(&self) -> bool { matches!( &self.0, @@ -116,38 +122,60 @@ impl TypePath { } } +/// The path of a Concrete type #[derive(Clone, Debug)] pub enum TypePathType { + /// A user-defined type (non-builtin struct or enum) Path { + /// Type path path: syn::Path, + /// Generic type parameters params: Vec, }, + /// A variable sized sequences of elements of some type. See [`std::vec::Vec`]. Vec { + /// Type of elements in the vector. of: Box, }, + /// A fixed length array that contains `len` elements of some type. Array { + /// number of elements in the array len: usize, + /// Type path of: Box, }, + /// A Tuple type Tuple { + /// Types that make up this tuple elements: Vec, }, + /// Primitive type Primitive { + /// A primitive Rust type. def: TypeDefPrimitive, }, + /// A compact encoded type Compact { + /// The type that is being compact encoded inner: Box, + /// is this type used as a field of a struct or enum right now? is_field: bool, + /// path to the `Compact` type (usually [`parity_scale_codec::Compact`]) compact_type_path: syn::Path, }, + /// A bit vector BitVec { + /// Order type bit_order_type: Box, + /// Store type bit_store_type: Box, + /// A user defined wrapper type around scale_bits::Bits. Should be generic over the `order` and `store` types. decoded_bits_type_path: syn::Path, }, } impl TypePathType { + /// Constructs a [`TypePathType`] from some context information. pub fn from_type_def_path( path: &Path, root_mod_ident: Ident, @@ -231,10 +259,12 @@ impl TypePathType { } } + /// Returns true, if this is a concrete compact type. pub fn is_compact(&self) -> bool { matches!(self, TypePathType::Compact { .. }) } + /// Returns true, if this is a string type. pub fn is_string(&self) -> bool { matches!( self, @@ -310,6 +340,7 @@ impl TypePathType { } } +/// A generic type parameter #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct TypeParameter { pub(super) concrete_type_id: u32, diff --git a/typegen/src/utils.rs b/typegen/src/utils.rs index 185ecc5..58c477b 100644 --- a/typegen/src/utils.rs +++ b/typegen/src/utils.rs @@ -4,12 +4,14 @@ use std::collections::HashMap; use crate::TypegenError; +/// Converts a [`scale_info::Type`] into a [`syn::TypePath`]. pub fn syn_type_path(ty: &Type) -> Result { let joined_path = ty.path.segments.join("::"); let ty_path: syn::TypePath = syn::parse_str(&joined_path)?; Ok(ty_path) } +/// Deduplicates type paths in the provided Registry. pub fn ensure_unique_type_paths(types: &mut PortableRegistry) { let mut types_with_same_type_path = HashMap::<&[String], SmallVec<[u32; 2]>>::new();