scale_decode/
lib.rs

1// Copyright (C) 2023 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#![no_std]
17
18/*!
19`parity-scale-codec` provides a `Decode` trait which allows bytes to be scale decoded into types based on the shape of those
20types. This crate builds on this, and allows bytes to be decoded into types based on type information, rather than the shape
21of the target type. At a high level, this crate just aims to do the reverse of the `scale-encode` crate.
22
23This crate exposes four traits:
24
25- A [`visitor::Visitor`] trait which when implemented on some type, can be used in conjunction with [`visitor::decode_with_visitor`]
26  to decode SCALE encoded bytes based on some type information into some arbitrary type.
27- An [`IntoVisitor`] trait which can be used to obtain the [`visitor::Visitor`] implementation for some type.
28- A [`DecodeAsType`] trait which is implemented for types which implement [`IntoVisitor`], and provides a high level interface for
29  decoding SCALE encoded bytes into some type with the help of a type ID and a [`scale_type_resolver::TypeResolver`].
30- A [`DecodeAsFields`] trait which when implemented on some type, describes how SCALE encoded bytes can be decoded
31  into it with the help of an iterator of [`Field`]s and a type registry describing the shape of the encoded bytes. This is
32  generally only implemented for tuples and structs, since we need a set of fields to map to the provided slices.
33
34Implementations for many built-in types are also provided for each trait, and the [`macro@DecodeAsType`] macro can be used to
35generate the relevant impls on new struct and enum types such that they get a [`DecodeAsType`] impl.
36
37The [`DecodeAsType`] and [`DecodeAsFields`] traits are basically the mirror of `scale-encode`'s `EncodeAsType` and `EncodeAsFields`
38traits in terms of their interface.
39
40# Motivation
41
42By de-coupling the shape of a type from how bytes are decoded into it, we make it much more likely that the decoding will succeed,
43and are no longer reliant on types having a precise layout in order to be decoded into correctly. Some examples of this follow.
44
45```rust
46use codec::Encode;
47use scale_decode::DecodeAsType;
48use scale_info::{PortableRegistry, TypeInfo};
49use std::fmt::Debug;
50
51// We normally expect to have type information to hand, but for our examples
52// we construct type info from any type that implements `TypeInfo`.
53fn get_type_info<T: TypeInfo + 'static>() -> (u32, PortableRegistry) {
54    let m = scale_info::MetaType::new::<T>();
55    let mut types = scale_info::Registry::new();
56    let ty = types.register_type(&m);
57    let portable_registry: PortableRegistry = types.into();
58    (ty.id, portable_registry)
59}
60
61// Encode the left value statically.
62// Decode those bytes into the right type via `DecodeAsType`.
63// Assert that the decoded bytes are identical to the right value.
64fn assert_decodes_to<A, B>(a: A, b: B)
65where
66    A: Encode + TypeInfo + 'static,
67    B: DecodeAsType + PartialEq + Debug,
68{
69    let (type_id, types) = get_type_info::<A>();
70    let a_bytes = a.encode();
71    let new_b = B::decode_as_type(&mut &*a_bytes, type_id, &types).unwrap();
72    assert_eq!(b, new_b);
73}
74
75// Start simple; a u8 can DecodeAsType into a u64 and vice versa. Numbers will all
76// try to convert into the desired output size, failing if this isn't possible:
77assert_decodes_to(123u64, 123u8);
78assert_decodes_to(123u8, 123u64);
79
80// Compact decoding is also handled "under the hood" by DecodeAsType, so no "compact"
81// annotations are needed on values.
82assert_decodes_to(codec::Compact(123u64), 123u64);
83
84// Enum variants are lined up by variant name, so no explicit "index" annotation are
85// needed either; DecodeAsType will take care of it.
86#[derive(Encode, TypeInfo)]
87enum Foo {
88    #[codec(index = 10)]
89    Something(u64),
90}
91#[derive(DecodeAsType, PartialEq, Debug)]
92enum FooTarget {
93    Something(u128),
94}
95assert_decodes_to(Foo::Something(123), FooTarget::Something(123));
96
97// DecodeAsType will skip annotated fields and not look for them in the encoded bytes.
98// #[codec(skip)] and #[decode_as_type(skip)] both work.
99#[derive(Encode, TypeInfo)]
100struct Bar {
101    a: bool,
102}
103#[derive(DecodeAsType, PartialEq, Debug)]
104struct BarTarget {
105    a: bool,
106    #[decode_as_type(skip)]
107    b: String,
108}
109assert_decodes_to(
110    Bar { a: true },
111    BarTarget { a: true, b: String::new() },
112);
113
114// DecodeAsType impls will generally skip through any newtype wrappers.
115#[derive(DecodeAsType, Encode, TypeInfo, PartialEq, Debug)]
116struct Wrapper {
117    value: u64
118}
119assert_decodes_to(
120    (Wrapper { value: 123 },),
121    123u64
122);
123assert_decodes_to(
124    123u64,
125    (123,)
126);
127
128// Things like arrays and sequences are generally interchangeable despite the
129// encoding format being slightly different:
130assert_decodes_to([1u8,2,3,4,5], vec![1u64,2,3,4,5]);
131assert_decodes_to(vec![1u64,2,3,4,5], [1u8,2,3,4,5]);
132```
133
134If this high level interface isn't suitable, you can implement your own [`visitor::Visitor`]'s. These can support zero-copy decoding
135(unlike the higher level [`DecodeAsType`] interface), and generally the Visitor construction and execution is zero alloc, allowing
136for efficient type based decoding.
137*/
138#![deny(missing_docs)]
139
140extern crate alloc;
141
142mod impls;
143
144pub mod error;
145pub mod visitor;
146
147pub use crate::error::Error;
148pub use scale_type_resolver::Field;
149pub use scale_type_resolver::FieldIter;
150pub use scale_type_resolver::TypeResolver;
151pub use visitor::Visitor;
152
153// This is exported for generated derive code to use, to be compatible with std or no-std as needed.
154#[doc(hidden)]
155pub use alloc::{collections::BTreeMap, string::ToString, vec};
156
157/// Re-exports of external crates.
158pub mod ext {
159    #[cfg(feature = "primitive-types")]
160    pub use primitive_types;
161    pub use scale_type_resolver;
162}
163
164/// This trait is implemented for any type `T` where `T` implements [`IntoVisitor`] and the errors returned
165/// from this [`Visitor`] can be converted into [`Error`]. It's essentially a convenience wrapper around
166/// [`visitor::decode_with_visitor`] that mirrors `scale-encode`'s `EncodeAsType`.
167pub trait DecodeAsType: Sized + IntoVisitor {
168    /// Given some input bytes, a `type_id`, and type registry, attempt to decode said bytes into
169    /// `Self`. Implementations should modify the `&mut` reference to the bytes such that any bytes
170    /// not used in the course of decoding are still pointed to after decoding is complete.
171    fn decode_as_type<R: TypeResolver>(
172        input: &mut &[u8],
173        type_id: R::TypeId,
174        types: &R,
175    ) -> Result<Self, Error> {
176        Self::decode_as_type_maybe_compact(input, type_id, types, false)
177    }
178
179    /// Given some input bytes, a `type_id`, and type registry, attempt to decode said bytes into
180    /// `Self`. Implementations should modify the `&mut` reference to the bytes such that any bytes
181    /// not used in the course of decoding are still pointed to after decoding is complete.
182    ///
183    /// If is_compact=true, it is assumed the value is compact encoded (only works for some types).
184    #[doc(hidden)]
185    fn decode_as_type_maybe_compact<R: TypeResolver>(
186        input: &mut &[u8],
187        type_id: R::TypeId,
188        types: &R,
189        is_compact: bool,
190    ) -> Result<Self, Error>;
191}
192
193impl<T: Sized + IntoVisitor> DecodeAsType for T {
194    fn decode_as_type_maybe_compact<R: TypeResolver>(
195        input: &mut &[u8],
196        type_id: R::TypeId,
197        types: &R,
198        is_compact: bool,
199    ) -> Result<Self, Error> {
200        let res = visitor::decode_with_visitor_maybe_compact(
201            input,
202            type_id,
203            types,
204            T::into_visitor::<R>(),
205            is_compact,
206        )?;
207        Ok(res)
208    }
209}
210
211/// This is similar to [`DecodeAsType`], except that it's instead implemented for types that can be given a list of
212/// fields denoting the type being decoded from and attempt to do this decoding. This is generally implemented just
213/// for tuple and struct types, and is automatically implemented via the [`macro@DecodeAsType`] macro.
214pub trait DecodeAsFields: Sized {
215    /// Given some bytes and some fields denoting their structure, attempt to decode.
216    fn decode_as_fields<'resolver, R: TypeResolver>(
217        input: &mut &[u8],
218        fields: &mut dyn FieldIter<'resolver, R::TypeId>,
219        types: &'resolver R,
220    ) -> Result<Self, Error>;
221}
222
223/// This trait can be implemented on any type that has an associated [`Visitor`] responsible for decoding
224/// SCALE encoded bytes to it whose error type is [`Error`]. Anything that implements this trait gets a
225/// [`DecodeAsType`] implementation for free.
226// Dev note: This used to allow for any Error type that could be converted into `scale_decode::Error`.
227// The problem with this is that the `DecodeAsType` trait became tricky to use in some contexts, because it
228// didn't automatically imply so much. Realistically, being stricter here shouldn't matter too much; derive
229// impls all use `scale_decode::Error` anyway, and manual impls can just manually convert into the error
230// rather than rely on auto conversion, if they care about also being able to impl `DecodeAsType`.
231pub trait IntoVisitor {
232    /// The visitor type used to decode SCALE encoded bytes to `Self`.
233    type AnyVisitor<R: TypeResolver>: for<'scale, 'resolver> visitor::Visitor<
234        Value<'scale, 'resolver> = Self,
235        Error = Error,
236        TypeResolver = R,
237    >;
238    /// A means of obtaining this visitor.
239    fn into_visitor<R: TypeResolver>() -> Self::AnyVisitor<R>;
240}
241
242/// The `DecodeAsType` derive macro can be used to implement `DecodeAsType` on structs and enums whose
243/// fields all implement `DecodeAsType`. Under the hood, the macro generates `scale_decode::visitor::Visitor`
244/// and `scale_decode::IntoVisitor` implementations for each type (as well as an associated `Visitor` struct),
245/// which in turn means that the type will automatically implement `scale_decode::DecodeAsType`.
246///
247/// # Examples
248///
249/// This can be applied to structs and enums:
250///
251/// ```rust
252/// use scale_decode::DecodeAsType;
253///
254/// #[derive(DecodeAsType)]
255/// struct Foo(String);
256///
257/// #[derive(DecodeAsType)]
258/// struct Bar {
259///     a: u64,
260///     b: bool
261/// }
262///
263/// #[derive(DecodeAsType)]
264/// enum Wibble<T> {
265///     A(usize, bool, T),
266///     B { value: String },
267///     C
268/// }
269/// ```
270///
271/// If you aren't directly depending on `scale_decode`, you must tell the macro what the path
272/// to it is so that it knows how to generate the relevant impls:
273///
274/// ```rust
275/// # use scale_decode as alt_path;
276/// use alt_path::DecodeAsType;
277///
278/// #[derive(DecodeAsType)]
279/// #[decode_as_type(crate_path = "alt_path")]
280/// struct Foo<T> {
281///    a: u64,
282///    b: T
283/// }
284/// ```
285///
286/// If you use generics, the macro will assume that each of them also implements `EncodeAsType`.
287/// This can be overridden when it's not the case (the compiler will ensure that you can't go wrong here):
288///
289/// ```rust
290/// use scale_decode::DecodeAsType;
291///
292/// #[derive(DecodeAsType)]
293/// #[decode_as_type(trait_bounds = "")]
294/// struct Foo<T> {
295///    a: u64,
296///    b: bool,
297///    #[decode_as_type(skip)]
298///    c: std::marker::PhantomData<T>
299/// }
300/// ```
301///
302/// You'll note that we can also opt to skip fields that we don't want to decode into; such fields will receive
303/// their default value and no attempt to decode SCALE bytes into them will occur.
304///
305/// # Attributes
306///
307/// - `#[decode_as_type(crate_path = "::path::to::scale_decode")]`:
308///   By default, the macro expects `scale_decode` to be a top level dependency,
309///   available as `::scale_decode`. If this is not the case, you can provide the
310///   crate path here.
311/// - `#[decode_as_type(trait_bounds = "T: Foo, U::Input: DecodeAsType")]`:
312///   By default, for each generate type parameter, the macro will add trait bounds such
313///   that these type parameters must implement `DecodeAsType` too. You can override this
314///   behaviour and provide your own trait bounds instead using this option.
315/// - `#[decode_as_type(skip)]` (or `#[codec(skip)]`):
316///   Any fields annotated with this will be skipped when attempting to decode into the
317///   type, and instead will be populated with their default value (and therefore must
318///   implement [`core::default::Default`]).
319#[cfg(feature = "derive")]
320pub use scale_decode_derive::DecodeAsType;