scale_type_resolver/
lib.rs

1// Copyright (C) 2024 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the scale-decode crate.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//         http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! `scale-type-resolver` provides a generic [`TypeResolver`] trait which can be implemented for
17//! any type that is capable of being given a type ID and resolving that into information about
18//! how the type is SCALE encoded. This allows libraries like `scale-decode` to be able to decode
19//! SCALE encoded bytes using either a modern type resolver like [`scale_info::PortableRegistry`],
20//! or using entirely custom type resolvers (which we would need in order decode blocks from pre-V14
21//! metadata).
22//!
23//! It's unlikely that you'd depend on this library directly; more likely you'd depend on a library
24//! like `scale-decode` which uses and re-exports the [`TypeResolver`] trait itself.
25//!
26//! This crate is `no_std` by default and doesn't require `alloc` except for tests.
27#![no_std]
28#![deny(missing_docs)]
29
30use core::iter::ExactSizeIterator;
31
32/// An implementation and associated things related to [`scale_info::PortableRegistry`].
33#[cfg(feature = "scale-info")]
34pub mod portable_registry;
35
36/// A concrete [`ResolvedTypeVisitor`] implementation that allows you to provide closures to
37/// configure it. Using this is often a lot easier than implementing [`ResolvedTypeVisitor`] yourself,
38/// but does require an additional dependency and may be a touch less performant.
39#[cfg(feature = "visitor")]
40pub mod visitor;
41
42/// This trait can be implemented for any type that is capable of describing how some type (identified
43/// by a [`TypeResolver::TypeId`]) is represented in terms of SCALE encoded bytes.
44///
45/// The naive way of implementing a trait like this would be to have a function that takes in a type ID
46/// and returns an enum which represents how the type is SCALE encoded. However, building such an enum
47/// to describe more complex types would likely involve allocating, which we'd rather avoid where possible.
48/// Instead, the approach taken here is to provide a set of callbacks to the [`TypeResolver::resolve_type()`]
49/// via a [`ResolvedTypeVisitor`] implementation. Exactly one of these callbacks is fired (and provided the
50/// necessary information) depending on the outcome of resolving a given type. This allows us to be far more
51/// flexible about how we hand back the required information, which avoids allocations.
52///
53/// For an example implementation, see the code in the [`portable_registry`] module.
54pub trait TypeResolver {
55    /// An identifier which points to some type that we'd like information on.
56    type TypeId: TypeId + 'static;
57    /// An error that can be handed back in the process of trying to resolve a type.
58    type Error: core::fmt::Debug + core::fmt::Display;
59
60    /// Given a type ID, this calls a method on the provided [`ResolvedTypeVisitor`] describing the
61    /// outcome of resolving the SCALE encoding information.
62    fn resolve_type<'this, V: ResolvedTypeVisitor<'this, TypeId = Self::TypeId>>(
63        &'this self,
64        type_id: Self::TypeId,
65        visitor: V,
66    ) -> Result<V::Value, Self::Error>;
67}
68
69/// A glorified set of callbacks, exactly one of which will fire depending on the outcome of calling
70/// [`TypeResolver::resolve_type()`]. These don't typically need to be implemented by the user, and
71/// instead are implemented internally in eg `scale-decode` to drive the decoding of types.
72///
73/// The lifetime held by this trait is the lifetime of the type resolver. Various inputs to the callbacks
74/// are thus bound by this lifetime, and it's possible to return values which contain this lifetime.
75pub trait ResolvedTypeVisitor<'resolver>: Sized {
76    /// The type ID type that the [`TypeResolver`] is using.
77    type TypeId: TypeId + 'static;
78    /// Some value that can be returned from the called method.
79    type Value;
80
81    /// Called when the actual method to be called was not implemented.
82    fn visit_unhandled(self, kind: UnhandledKind) -> Self::Value;
83
84    /// Called when the type ID passed to [`TypeResolver::resolve_type()`] was not found.
85    fn visit_not_found(self) -> Self::Value {
86        self.visit_unhandled(UnhandledKind::NotFound)
87    }
88
89    /// Called when the type ID corresponds to a composite type. This is provided an iterator
90    /// over each of the fields and their type IDs that the composite type contains.
91    fn visit_composite<Path, Fields>(self, _path: Path, _fields: Fields) -> Self::Value
92    where
93        Path: PathIter<'resolver>,
94        Fields: FieldIter<'resolver, Self::TypeId>,
95    {
96        self.visit_unhandled(UnhandledKind::Composite)
97    }
98
99    /// Called when the type ID corresponds to a variant type. This is provided the list of
100    /// variants (and for each variant, the fields within it) that the type could be encoded as.
101    fn visit_variant<Path, Fields, Var>(self, _path: Path, _variants: Var) -> Self::Value
102    where
103        Path: PathIter<'resolver>,
104        Fields: FieldIter<'resolver, Self::TypeId>,
105        Var: VariantIter<'resolver, Fields>,
106    {
107        self.visit_unhandled(UnhandledKind::Variant)
108    }
109
110    /// Called when the type ID corresponds to a sequence of values of some type ID (which is
111    /// provided as an argument here). The length of the sequence is compact encoded first.
112    fn visit_sequence<Path>(self, _path: Path, _type_id: Self::TypeId) -> Self::Value
113    where
114        Path: PathIter<'resolver>,
115    {
116        self.visit_unhandled(UnhandledKind::Sequence)
117    }
118
119    /// Called when the type ID corresponds to an array of values of some type ID (which is
120    /// provided as an argument here). The length of the array is known at compile time and
121    /// is also provided.
122    fn visit_array(self, _type_id: Self::TypeId, _len: usize) -> Self::Value {
123        self.visit_unhandled(UnhandledKind::Array)
124    }
125
126    /// Called when the type ID corresponds to a tuple of values whose type IDs are provided here.
127    fn visit_tuple<TypeIds>(self, _type_ids: TypeIds) -> Self::Value
128    where
129        TypeIds: ExactSizeIterator<Item = Self::TypeId>,
130    {
131        self.visit_unhandled(UnhandledKind::Tuple)
132    }
133
134    /// Called when the type ID corresponds to some primitive type. The exact primitive type is
135    /// provided in th form of an enum.
136    fn visit_primitive(self, _primitive: Primitive) -> Self::Value {
137        self.visit_unhandled(UnhandledKind::Primitive)
138    }
139
140    /// Called when the type ID corresponds to a compact encoded type. The type ID of the inner type
141    /// is provided.
142    fn visit_compact(self, _type_id: Self::TypeId) -> Self::Value {
143        self.visit_unhandled(UnhandledKind::Compact)
144    }
145
146    /// Called when the type ID corresponds to a bit sequence, whose store and order types are given.
147    fn visit_bit_sequence(
148        self,
149        _store_format: BitsStoreFormat,
150        _order_format: BitsOrderFormat,
151    ) -> Self::Value {
152        self.visit_unhandled(UnhandledKind::BitSequence)
153    }
154}
155
156/// If any of the [`ResolvedTypeVisitor`] methods are not implemented, then
157/// [`ResolvedTypeVisitor::visit_unhandled()`] is called, and given an instance of this enum to
158/// denote which method was unhandled.
159#[allow(missing_docs)]
160#[derive(Clone, Copy, PartialEq, Eq, Debug)]
161pub enum UnhandledKind {
162    NotFound,
163    Composite,
164    Variant,
165    Sequence,
166    Array,
167    Tuple,
168    Primitive,
169    Compact,
170    BitSequence,
171}
172
173/// A trait representing a type ID.
174pub trait TypeId: Clone + core::fmt::Debug + core::default::Default {}
175impl<T: Clone + core::fmt::Debug + core::default::Default> TypeId for T {}
176
177/// Information about a composite field.
178#[derive(Debug)]
179pub struct Field<'resolver, TypeId> {
180    /// The name of the field, or `None` if the field is unnamed.
181    pub name: Option<&'resolver str>,
182    /// The type ID corresponding to the value for this field.
183    pub id: TypeId,
184}
185
186impl<'resolver, TypeId: Copy> Copy for Field<'resolver, TypeId> {}
187impl<'resolver, TypeId: Clone> Clone for Field<'resolver, TypeId> {
188    fn clone(&self) -> Self {
189        Field {
190            name: self.name,
191            id: self.id.clone(),
192        }
193    }
194}
195
196impl<'resolver, TypeId> Field<'resolver, TypeId> {
197    /// Construct a new field with an ID and optional name.
198    pub fn new(id: TypeId, name: Option<&'resolver str>) -> Self {
199        Field { id, name }
200    }
201    /// Create a new unnamed field.
202    pub fn unnamed(id: TypeId) -> Self {
203        Field { name: None, id }
204    }
205    /// Create a new named field.
206    pub fn named(id: TypeId, name: &'resolver str) -> Self {
207        Field {
208            name: Some(name),
209            id,
210        }
211    }
212}
213
214/// Information about a specific variant type.
215#[derive(Clone, Debug)]
216pub struct Variant<'resolver, Fields> {
217    /// The variant index.
218    pub index: u8,
219    /// The variant name.
220    pub name: &'resolver str,
221    /// The fields contained within this variant.
222    pub fields: Fields,
223}
224
225/// An iterator over the path parts.
226pub trait PathIter<'resolver>: Iterator<Item = &'resolver str> {}
227impl<'resolver, T> PathIter<'resolver> for T where T: Iterator<Item = &'resolver str> {}
228
229/// An iterator over a set of fields.
230pub trait FieldIter<'resolver, TypeId>: ExactSizeIterator<Item = Field<'resolver, TypeId>> {}
231impl<'resolver, TypeId, T> FieldIter<'resolver, TypeId> for T where
232    T: ExactSizeIterator<Item = Field<'resolver, TypeId>>
233{
234}
235
236/// An iterator over a set of variants.
237pub trait VariantIter<'resolver, Fields>:
238    ExactSizeIterator<Item = Variant<'resolver, Fields>>
239{
240}
241impl<'resolver, Fields, T> VariantIter<'resolver, Fields> for T where
242    T: ExactSizeIterator<Item = Variant<'resolver, Fields>>
243{
244}
245
246/// This is handed to [`ResolvedTypeVisitor::visit_primitive()`], and
247/// denotes the exact shape of the primitive type that we have resolved.
248#[allow(missing_docs)]
249#[derive(Clone, Copy, PartialEq, Eq, Debug)]
250pub enum Primitive {
251    Bool,
252    Char,
253    Str,
254    U8,
255    U16,
256    U32,
257    U64,
258    U128,
259    U256,
260    I8,
261    I16,
262    I32,
263    I64,
264    I128,
265    I256,
266}
267
268/// This is a runtime representation of the order that bits will be written
269/// to the specified [`BitsStoreFormat`].
270///
271/// - [`BitsOrderFormat::Lsb0`] means that we write to the least significant bit first
272///   and then work our way up to the most significant bit as we push new bits.
273/// - [`BitsOrderFormat::Msb0`] means that we write to the most significant bit first
274///   and then work our way down to the least significant bit as we push new bits.
275///
276/// These are equivalent to `bitvec`'s `order::Lsb0` and `order::Msb0`.
277#[derive(Copy, Clone, PartialEq, Eq, Debug)]
278pub enum BitsOrderFormat {
279    /// Least significant bit first.
280    Lsb0,
281    /// Most significant bit first.
282    Msb0,
283}
284
285/// This is a runtime representation of the store type that we're targeting. These
286/// are equivalent to the `bitvec` store types `u8`, `u16` and so on.
287#[derive(Copy, Clone, PartialEq, Eq, Debug)]
288pub enum BitsStoreFormat {
289    /// Equivalent to [`u8`].
290    U8,
291    /// Equivalent to [`u16`].
292    U16,
293    /// Equivalent to [`u32`].
294    U32,
295    /// Equivalent to [`u64`].
296    U64,
297}