Skip to content

Commit

Permalink
0.2: TypeId now passed by value and path provided to named types (#5)
Browse files Browse the repository at this point in the history
* Expect TypeId to be copied/cloned instead of passed by ref and reduce lifetimes

* provide the path of the type to the sequence/composite/variant callbacks

* Make the path have a lifetime of 'resolver

* 'resovler for path in concrete visitor too

* bump to 0.2
  • Loading branch information
jsdw authored Apr 29, 2024
1 parent 5c8d78e commit d8e391e
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 89 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 0.2.0

- Provide a `path` iterator to composite, variant and sequence callbacks in `ResolveTypeVisitor`, so that people can access the path/name of these types. The path is also exposed in the concrete visitor implementation.
- Make `TypeId` be passed by value and not lifetime. This is done because:
1. `scale-info` uses a `TypeId = u32`, so it does not matter either way, and
2. `scale-info-legacy` uses `TypeId = TypeName`, and needs to modify the `TypeName`s provided back in some cases, making pass-by-reference impossible.

# 0.1.1

Just a patch release to make a small addition and no API changes:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "scale-type-resolver"
description = "A low level trait to be generic over how to resolve SCALE type information"
version = "0.1.1"
version = "0.2.0"
edition = "2021"
authors = ["Parity Technologies <admin@parity.io>"]
license = "Apache-2.0"
Expand Down
63 changes: 36 additions & 27 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ pub mod visitor;
/// For an example implementation, see the code in the [`portable_registry`] module.
pub trait TypeResolver {
/// An identifier which points to some type that we'd like information on.
type TypeId: TypeId;
type TypeId: TypeId + 'static;
/// An error that can be handed back in the process of trying to resolve a type.
type Error: core::fmt::Debug + core::fmt::Display;

/// Given a type ID, this calls a method on the provided [`ResolvedTypeVisitor`] describing the
/// outcome of resolving the SCALE encoding information.
fn resolve_type<'this, V: ResolvedTypeVisitor<'this, TypeId = Self::TypeId>>(
&'this self,
type_id: &Self::TypeId,
type_id: Self::TypeId,
visitor: V,
) -> Result<V::Value, Self::Error>;
}
Expand All @@ -74,7 +74,7 @@ pub trait TypeResolver {
/// are thus bound by this lifetime, and it's possible to return values which contain this lifetime.
pub trait ResolvedTypeVisitor<'resolver>: Sized {
/// The type ID type that the [`TypeResolver`] is using.
type TypeId: TypeId + 'resolver;
type TypeId: TypeId + 'static;
/// Some value that can be returned from the called method.
type Value;

Expand All @@ -88,17 +88,19 @@ pub trait ResolvedTypeVisitor<'resolver>: Sized {

/// Called when the type ID corresponds to a composite type. This is provided an iterator
/// over each of the fields and their type IDs that the composite type contains.
fn visit_composite<Fields>(self, _fields: Fields) -> Self::Value
fn visit_composite<Path, Fields>(self, _path: Path, _fields: Fields) -> Self::Value
where
Path: PathIter<'resolver>,
Fields: FieldIter<'resolver, Self::TypeId>,
{
self.visit_unhandled(UnhandledKind::Composite)
}

/// Called when the type ID corresponds to a variant type. This is provided the list of
/// variants (and for each variant, the fields within it) that the type could be encoded as.
fn visit_variant<Fields: 'resolver, Var>(self, _variants: Var) -> Self::Value
fn visit_variant<Path, Fields, Var>(self, _path: Path, _variants: Var) -> Self::Value
where
Path: PathIter<'resolver>,
Fields: FieldIter<'resolver, Self::TypeId>,
Var: VariantIter<'resolver, Fields>,
{
Expand All @@ -107,21 +109,24 @@ pub trait ResolvedTypeVisitor<'resolver>: Sized {

/// Called when the type ID corresponds to a sequence of values of some type ID (which is
/// provided as an argument here). The length of the sequence is compact encoded first.
fn visit_sequence(self, _type_id: &'resolver Self::TypeId) -> Self::Value {
fn visit_sequence<Path>(self, _path: Path, _type_id: Self::TypeId) -> Self::Value
where
Path: PathIter<'resolver>,
{
self.visit_unhandled(UnhandledKind::Sequence)
}

/// Called when the type ID corresponds to an array of values of some type ID (which is
/// provided as an argument here). The length of the array is known at compile time and
/// is also provided.
fn visit_array(self, _type_id: &'resolver Self::TypeId, _len: usize) -> Self::Value {
fn visit_array(self, _type_id: Self::TypeId, _len: usize) -> Self::Value {
self.visit_unhandled(UnhandledKind::Array)
}

/// Called when the type ID corresponds to a tuple of values whose type IDs are provided here.
fn visit_tuple<TypeIds>(self, _type_ids: TypeIds) -> Self::Value
where
TypeIds: ExactSizeIterator<Item = &'resolver Self::TypeId>,
TypeIds: ExactSizeIterator<Item = Self::TypeId>,
{
self.visit_unhandled(UnhandledKind::Tuple)
}
Expand All @@ -134,7 +139,7 @@ pub trait ResolvedTypeVisitor<'resolver>: Sized {

/// Called when the type ID corresponds to a compact encoded type. The type ID of the inner type
/// is provided.
fn visit_compact(self, _type_id: &'resolver Self::TypeId) -> Self::Value {
fn visit_compact(self, _type_id: Self::TypeId) -> Self::Value {
self.visit_unhandled(UnhandledKind::Compact)
}

Expand Down Expand Up @@ -166,36 +171,39 @@ pub enum UnhandledKind {
}

/// A trait representing a type ID.
pub trait TypeId: core::fmt::Debug + core::default::Default {}
impl<T: core::fmt::Debug + core::default::Default> TypeId for T {}
pub trait TypeId: Clone + core::fmt::Debug + core::default::Default {}
impl<T: Clone + core::fmt::Debug + core::default::Default> TypeId for T {}

/// Information about a composite field.
#[derive(Debug)]
pub struct Field<'resolver, TypeId: 'resolver> {
pub struct Field<'resolver, TypeId> {
/// The name of the field, or `None` if the field is unnamed.
pub name: Option<&'resolver str>,
/// The type ID corresponding to the value for this field.
pub id: &'resolver TypeId,
pub id: TypeId,
}

impl<'resolver, TypeId: 'resolver> Copy for Field<'resolver, TypeId> {}
impl<'resolver, TypeId: 'resolver> Clone for Field<'resolver, TypeId> {
impl<'resolver, TypeId: Copy> Copy for Field<'resolver, TypeId> {}
impl<'resolver, TypeId: Clone> Clone for Field<'resolver, TypeId> {
fn clone(&self) -> Self {
*self
Field {
name: self.name,
id: self.id.clone(),
}
}
}

impl<'resolver, TypeId> Field<'resolver, TypeId> {
/// Construct a new field with an ID and optional name.
pub fn new(id: &'resolver TypeId, name: Option<&'resolver str>) -> Self {
pub fn new(id: TypeId, name: Option<&'resolver str>) -> Self {
Field { id, name }
}
/// Create a new unnamed field.
pub fn unnamed(id: &'resolver TypeId) -> Self {
pub fn unnamed(id: TypeId) -> Self {
Field { name: None, id }
}
/// Create a new named field.
pub fn named(id: &'resolver TypeId, name: &'resolver str) -> Self {
pub fn named(id: TypeId, name: &'resolver str) -> Self {
Field {
name: Some(name),
id,
Expand All @@ -205,7 +213,7 @@ impl<'resolver, TypeId> Field<'resolver, TypeId> {

/// Information about a specific variant type.
#[derive(Clone, Debug)]
pub struct Variant<'resolver, Fields: 'resolver> {
pub struct Variant<'resolver, Fields> {
/// The variant index.
pub index: u8,
/// The variant name.
Expand All @@ -214,22 +222,23 @@ pub struct Variant<'resolver, Fields: 'resolver> {
pub fields: Fields,
}

/// An iterator over the path parts.
pub trait PathIter<'resolver>: Iterator<Item = &'resolver str> {}
impl<'resolver, T> PathIter<'resolver> for T where T: Iterator<Item = &'resolver str> {}

/// An iterator over a set of fields.
pub trait FieldIter<'resolver, TypeId: 'resolver>:
ExactSizeIterator<Item = Field<'resolver, TypeId>>
{
}
impl<'resolver, TypeId: 'resolver, T> FieldIter<'resolver, TypeId> for T where
pub trait FieldIter<'resolver, TypeId>: ExactSizeIterator<Item = Field<'resolver, TypeId>> {}
impl<'resolver, TypeId, T> FieldIter<'resolver, TypeId> for T where
T: ExactSizeIterator<Item = Field<'resolver, TypeId>>
{
}

/// An iterator over a set of variants.
pub trait VariantIter<'resolver, Fields: 'resolver>:
pub trait VariantIter<'resolver, Fields>:
ExactSizeIterator<Item = Variant<'resolver, Fields>>
{
}
impl<'resolver, Fields: 'resolver, T> VariantIter<'resolver, Fields> for T where
impl<'resolver, Fields, T> VariantIter<'resolver, Fields> for T where
T: ExactSizeIterator<Item = Variant<'resolver, Fields>>
{
}
Expand Down
36 changes: 20 additions & 16 deletions src/portable_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,36 +62,39 @@ impl TypeResolver for PortableRegistry {

fn resolve_type<'this, V: ResolvedTypeVisitor<'this, TypeId = Self::TypeId>>(
&'this self,
type_id: &Self::TypeId,
type_id: Self::TypeId,
visitor: V,
) -> Result<V::Value, Self::Error> {
let type_id = *type_id;
let Some(ty) = self.resolve(type_id) else {
return Ok(visitor.visit_not_found());
};

let path_iter = ty.path.segments.iter().map(|s| s.as_ref());

let val = match &ty.type_def {
scale_info::TypeDef::Composite(composite) => {
visitor.visit_composite(iter_fields(&composite.fields))
visitor.visit_composite(path_iter, iter_fields(&composite.fields))
}
scale_info::TypeDef::Variant(variant) => {
visitor.visit_variant(iter_variants(&variant.variants))
visitor.visit_variant(path_iter, iter_variants(&variant.variants))
}
scale_info::TypeDef::Sequence(seq) => {
visitor.visit_sequence(path_iter, seq.type_param.id)
}
scale_info::TypeDef::Sequence(seq) => visitor.visit_sequence(&seq.type_param.id),
scale_info::TypeDef::Array(arr) => {
visitor.visit_array(&arr.type_param.id, arr.len as usize)
visitor.visit_array(arr.type_param.id, arr.len as usize)
}
scale_info::TypeDef::Tuple(tuple) => {
let ids = tuple.fields.iter().map(|f| &f.id);
let ids = tuple.fields.iter().map(|f| f.id);
visitor.visit_tuple(ids)
}
scale_info::TypeDef::Primitive(prim) => {
let primitive = into_primitive(prim);
visitor.visit_primitive(primitive)
}
scale_info::TypeDef::Compact(compact) => visitor.visit_compact(&compact.type_param.id),
scale_info::TypeDef::BitSequence(ty) => {
let (order, store) = bits_from_metadata(ty, self)?;
scale_info::TypeDef::Compact(compact) => visitor.visit_compact(compact.type_param.id),
scale_info::TypeDef::BitSequence(bitseq) => {
let (order, store) = bits_from_metadata(bitseq, self)?;
visitor.visit_bit_sequence(store, order)
}
};
Expand Down Expand Up @@ -135,7 +138,7 @@ fn iter_fields(
) -> impl ExactSizeIterator<Item = Field<'_, u32>> {
fields.iter().map(|f| Field {
name: f.name.as_deref(),
id: &f.ty.id,
id: f.ty.id,
})
}

Expand Down Expand Up @@ -201,18 +204,18 @@ mod test {

fn assert_type<T: scale_info::TypeInfo + 'static>(info: ResolvedTypeInfo) {
let (id, types) = make_type::<T>();
let resolved_info = to_resolved_info(&id, &types);
let resolved_info = to_resolved_info(id, &types);
assert_eq!(info, resolved_info);
}

fn to_resolved_info(type_id: &u32, types: &PortableRegistry) -> ResolvedTypeInfo {
fn to_resolved_info(type_id: u32, types: &PortableRegistry) -> ResolvedTypeInfo {
use crate::visitor;

// Build a quick visitor which turns resolved type info
// into a concrete type for us to check.
let visitor = visitor::new((), |_, _| panic!("all methods implemented"))
.visit_not_found(|_| ResolvedTypeInfo::NotFound)
.visit_composite(|_, fields| {
.visit_composite(|_, _, fields| {
let fs = fields
.map(|f| {
let inner_ty = to_resolved_info(f.id, types);
Expand All @@ -221,7 +224,7 @@ mod test {
.collect();
ResolvedTypeInfo::CompositeOf(fs)
})
.visit_variant(|_, variants| {
.visit_variant(|_, _, variants| {
let vs = variants
.map(|v| {
let fs: Vec<_> = v
Expand All @@ -236,7 +239,7 @@ mod test {
.collect();
ResolvedTypeInfo::VariantOf(vs)
})
.visit_sequence(|_, type_id| {
.visit_sequence(|_, _, type_id| {
ResolvedTypeInfo::SequenceOf(Box::new(to_resolved_info(type_id, types)))
})
.visit_array(|_, type_id, len| {
Expand Down Expand Up @@ -302,6 +305,7 @@ mod test {
]));

#[derive(scale_info::TypeInfo)]
#[allow(dead_code)]
struct Unnamed(bool, u8);

assert_type::<Unnamed>(ResolvedTypeInfo::CompositeOf(vec![
Expand Down
Loading

0 comments on commit d8e391e

Please sign in to comment.