scale_encode/
lib.rs

1// Copyright (C) 2023 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the scale-encode 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 an `Encode` trait which allows types to SCALE encode themselves based on their shape.
20This crate builds on this, and allows types to encode themselves based on type information from a [`TypeResolver`]
21implementation (one such implementation being a `scale_info::PortableRegistry`). It exposes two traits:
22
23- An [`EncodeAsType`] trait which when implemented on some type, describes how it can be SCALE encoded
24  with the help of a type ID and type registry describing the expected shape of the encoded bytes.
25- An [`EncodeAsFields`] trait which when implemented on some type, describes how it can be SCALE encoded
26  with the help of an iterator over [`Field`]s and a type registry describing the expected shape of the
27  encoded bytes. This is generally only implemented for tuples and structs, since we need a set of fields
28  to map to the provided iterator.
29
30Implementations for many built-in types are also provided for each trait, and the [`macro@EncodeAsType`]
31macro makes it easy to generate implementations for new structs and enums.
32
33# Motivation
34
35By de-coupling the shape of a type from how it's encoded, we make it much more likely that encoding some type will succeed,
36and are no longer reliant on types having a precise layout in order to encode correctly. Some examples of this follow.
37
38```rust
39use codec::Encode;
40use scale_encode::EncodeAsType;
41use scale_info::{PortableRegistry, TypeInfo};
42
43// We are commonly provided type information, but for our examples we construct type info from
44// any type that implements `TypeInfo`.
45fn get_type_info<T: TypeInfo + 'static>() -> (u32, PortableRegistry) {
46    let m = scale_info::MetaType::new::<T>();
47    let mut types = scale_info::Registry::new();
48    let ty = types.register_type(&m);
49    let portable_registry: PortableRegistry = types.into();
50    (ty.id, portable_registry)
51}
52
53// Encode the left value via EncodeAsType into the shape of the right value.
54// Encode the right value statically.
55// Assert that both outputs are identical.
56fn assert_encodes_to<A, B>(a: A, b: B)
57where
58    A: EncodeAsType,
59    B: TypeInfo + Encode + 'static,
60{
61    let (type_id, types) = get_type_info::<B>();
62    let a_bytes = a.encode_as_type(type_id, &types).unwrap();
63    let b_bytes = b.encode();
64    assert_eq!(a_bytes, b_bytes);
65}
66
67// Start simple; a u8 can EncodeAsType into a u64 and vice versa. Numbers will all
68// try to convert into the desired output size, failing if this isn't possible:
69assert_encodes_to(123u8, 123u64);
70assert_encodes_to(123u64, 123u8);
71
72// Compact encoding is also handled "under the hood" by EncodeAsType, so no "compact"
73// annotations are needed on values.
74assert_encodes_to(123u64, codec::Compact(123u64));
75
76// Enum variants are lined up by variant name, so no explicit "index" annotation are
77// needed either; EncodeAsType will take care of it.
78#[derive(EncodeAsType)]
79enum Foo {
80    Something(u64),
81}
82#[derive(Encode, TypeInfo)]
83enum FooTarget {
84    #[codec(index = 10)]
85    Something(u128),
86}
87assert_encodes_to(Foo::Something(123), FooTarget::Something(123));
88
89// EncodeAstype will just ignore named fields that aren't needed:
90#[derive(EncodeAsType)]
91struct Bar {
92    a: bool,
93    b: String,
94}
95#[derive(Encode, TypeInfo)]
96struct BarTarget {
97    a: bool,
98}
99assert_encodes_to(
100    Bar { a: true, b: "hello".to_string() },
101    BarTarget { a: true },
102);
103
104// EncodeAsType will attempt to remove any newtype wrappers and such on either
105// side, so that they can be omitted without any issue.
106#[derive(EncodeAsType, Encode, TypeInfo)]
107struct Wrapper {
108    value: u64
109}
110assert_encodes_to(
111    (Wrapper { value: 123 },),
112    123u64
113);
114assert_encodes_to(
115    123u64,
116    (Wrapper { value: 123 },)
117);
118
119// Things like arrays and sequences are generally interchangeable despite the
120// encoding format being slightly different:
121assert_encodes_to([1u8,2,3,4,5], vec![1u64,2,3,4,5]);
122assert_encodes_to(vec![1u64,2,3,4,5], [1u8,2,3,4,5]);
123
124// BTreeMap, as a slightly special case, can encode to the same shape as either
125// a sequence or a struct, depending on what's asked for:
126use std::collections::BTreeMap;
127#[derive(TypeInfo, Encode)]
128struct MapOutput {
129    a: u64,
130    b: u64
131}
132assert_encodes_to(
133    BTreeMap::from_iter([("a", 1u64), ("b", 2u64)]),
134    vec![1u64,2]
135);
136assert_encodes_to(
137    BTreeMap::from_iter([("a", 1u64), ("b", 2u64), ("c", 3u64)]),
138    MapOutput { a: 1, b: 2 }
139);
140```
141*/
142#![deny(missing_docs)]
143
144extern crate alloc;
145
146mod impls;
147
148pub mod error;
149
150// This is exported for generated derive code to use, to be compatible with std or no-std as needed.
151#[doc(hidden)]
152pub use alloc::vec::Vec;
153
154pub use error::Error;
155
156// Useful types to help implement EncodeAsType/Fields with:
157pub use crate::impls::{Composite, CompositeField, Variant};
158pub use scale_type_resolver::{Field, FieldIter, TypeResolver};
159
160/// Re-exports of external crates.
161pub mod ext {
162    #[cfg(feature = "primitive-types")]
163    pub use primitive_types;
164}
165
166/// This trait signals that some static type can possibly be SCALE encoded given some
167/// `type_id` and a corresponding [`TypeResolver`] which tells us about the expected encoding.
168pub trait EncodeAsType {
169    /// Given some `type_id`, `types`, a `context` and some output target for the SCALE encoded bytes,
170    /// attempt to SCALE encode the current value into the type given by `type_id`.
171    fn encode_as_type_to<R: TypeResolver>(
172        &self,
173        type_id: R::TypeId,
174        types: &R,
175        out: &mut Vec<u8>,
176    ) -> Result<(), Error>;
177
178    /// This is a helper function which internally calls [`EncodeAsType::encode_as_type_to`]. Prefer to
179    /// implement that instead.
180    fn encode_as_type<R: TypeResolver>(
181        &self,
182        type_id: R::TypeId,
183        types: &R,
184    ) -> Result<Vec<u8>, Error> {
185        let mut out = Vec::new();
186        self.encode_as_type_to(type_id, types, &mut out)?;
187        Ok(out)
188    }
189}
190
191/// This is similar to [`EncodeAsType`], except that it can be implemented on types that can be encoded
192/// to bytes given a list of fields instead of a single type ID. This is generally implemented just for
193/// tuple and struct types, and is automatically implemented via the [`macro@EncodeAsType`] macro.
194pub trait EncodeAsFields {
195    /// Given some fields describing the shape of a type, attempt to encode to that shape.
196    fn encode_as_fields_to<R: TypeResolver>(
197        &self,
198        fields: &mut dyn FieldIter<'_, R::TypeId>,
199        types: &R,
200        out: &mut Vec<u8>,
201    ) -> Result<(), Error>;
202
203    /// This is a helper function which internally calls [`EncodeAsFields::encode_as_fields_to`]. Prefer to
204    /// implement that instead.
205    fn encode_as_fields<R: TypeResolver>(
206        &self,
207        fields: &mut dyn FieldIter<'_, R::TypeId>,
208        types: &R,
209    ) -> Result<Vec<u8>, Error> {
210        let mut out = Vec::new();
211        self.encode_as_fields_to(fields, types, &mut out)?;
212        Ok(out)
213    }
214}
215
216/// The `EncodeAsType` derive macro can be used to implement `EncodeAsType`
217/// on structs and enums whose fields all implement `EncodeAsType`.
218///
219/// # Examples
220///
221/// This can be applied to structs and enums:
222///
223/// ```rust
224/// use scale_encode::EncodeAsType;
225///
226/// #[derive(EncodeAsType)]
227/// struct Foo(String);
228///
229/// #[derive(EncodeAsType)]
230/// struct Bar {
231///     a: u64,
232///     b: bool
233/// }
234///
235/// #[derive(EncodeAsType)]
236/// enum Wibble<T> {
237///     A(usize, bool, T),
238///     B { value: String },
239///     C
240/// }
241/// ```
242///
243/// If you aren't directly depending on `scale_encode`, you must tell the macro what the path
244/// to it is so that it knows how to generate the relevant impls:
245///
246/// ```rust
247/// # use scale_encode as alt_path;
248/// use alt_path::EncodeAsType;
249///
250/// #[derive(EncodeAsType)]
251/// #[encode_as_type(crate_path = "alt_path")]
252/// struct Foo<T> {
253///    a: u64,
254///    b: T
255/// }
256/// ```
257///
258/// If you use generics, the macro will assume that each of them also implements `EncodeAsType`.
259/// This can be overridden when it's not the case (the compiler will ensure that you can't go wrong here):
260///
261/// ```rust
262/// use scale_encode::EncodeAsType;
263///
264/// #[derive(EncodeAsType)]
265/// #[encode_as_type(trait_bounds = "")]
266/// struct Foo<T> {
267///    a: u64,
268///    b: bool,
269///    c: std::marker::PhantomData<T>
270/// }
271/// ```
272///
273/// # Attributes
274///
275/// - `#[encode_as_type(crate_path = "::path::to::scale_encode")]`:
276///   By default, the macro expects `scale_encode` to be a top level dependency,
277///   available as `::scale_encode`. If this is not the case, you can provide the
278///   crate path here.
279/// - `#[encode_as_type(trait_bounds = "T: Foo, U::Input: EncodeAsType")]`:
280///   By default, for each generate type parameter, the macro will add trait bounds such
281///   that these type parameters must implement `EncodeAsType` too. You can override this
282///   behaviour and provide your own trait bounds instead using this option.
283#[cfg(feature = "derive")]
284pub use scale_encode_derive::EncodeAsType;