eosio_scale_info/lib.rs
1// Copyright 2019-2022 Parity Technologies (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![cfg_attr(not(feature = "std"), no_std)]
16#![deny(missing_docs)]
17
18//! Efficient and space-efficient serialization of Rust types.
19//!
20//! This library provides structures to easily retrieve compile-time type
21//! information at runtime and also to serialize this information in a
22//! space-efficient form, aka `PortableForm`.
23//!
24//! # Registry
25//!
26//! At the heart of its functionality is the [`Registry`](`crate::Registry`)
27//! that acts as a cache for known types in order to efficiently deduplicate
28//! types and ensure a space-efficient serialization.
29//!
30//! # Type Information
31//!
32//! Information about types is provided via the [`TypeInfo`](`crate::TypeInfo`)
33//! trait.
34//!
35//! This trait should be implemented for all types that are serializable.
36//! `scale-info` provides implementations for all commonly used Rust standard
37//! types and a derive macro for implementing of custom types.
38//!
39//! ## Deriving `TypeInfo`
40//!
41//! Enable the `derive` feature of this crate:
42//!
43//! ```toml
44//! scale-info = { version = "2.0.0", features = ["derive"] }
45//! ```
46//!
47//! ```ignore
48//! use eosio_scale_info::TypeInfo;
49//!
50//! #[derive(TypeInfo)]
51//! struct MyStruct {
52//! a: u32,
53//! b: MyEnum,
54//! }
55//!
56//! #[derive(TypeInfo)]
57//! enum MyEnum {
58//! A(bool),
59//! B { f: Vec<u8> },
60//! C,
61//! }
62//! ```
63//!
64//! ### Attributes
65//!
66//! #### `#[scale_info(bounds(..))]`
67//!
68//! Replace the auto-generated `where` clause bounds for the derived `TypeInfo` implementation.
69//!
70//! ```ignore
71//! #[derive(TypeInfo)]
72//! #[scale_info(bounds(T: TypeInfo + 'static))]
73//! struct MyStruct<T> {
74//! a: Vec<T>,
75//! }
76//! ```
77//!
78//! The derive macro automatically adds `TypeInfo` bounds for all type parameters, and all field
79//! types containing type parameters or associated types.
80//!
81//! This is naive and sometimes adds unnecessary bounds, since on a syntactical level there is no
82//! way to differentiate between a generic type constructor and a type alias with a generic argument
83//! e.g.
84//!
85//! ```ignore
86//! trait MyTrait {
87//! type A;
88//! }
89//!
90//! type MyAlias<T> = <T as MyTrait>::A;
91//!
92//! #[derive(TypeInfo)]
93//! struct MyStruct<T> {
94//! a: MyAlias<T>,
95//! b: Vec<T>,
96//! }
97//! ```
98//!
99//! So for the above, since a `MyAlias<T>: TypeInfo` bound is required, and we can't distinguish
100//! between `MyAlias<T>` and `Vec<T>`, then the `TypeInfo` bound is simply added for all
101//! fields which contain any type param. In this case the redundant `Vec<T>: TypeInfo`
102//! would be added.
103//!
104//! This is usually okay, but in some circumstances can cause problems, for example with the
105//! [`overflow evaluating the requirement`] error [here](https://github.com/paritytech/scale-info/blob/master/test_suite/tests/ui/pass_custom_bounds_fix_overflow.rs).
106//!
107//! The `bounds` attribute provides an ["escape hatch"](https://serde.rs/attr-bound.html) to allow
108//! the programmer control of the `where` clause on the generated `impl`, to solve this and other
109//! issues that can't be foreseen by the auto-generated bounds heuristic.
110//!
111//! #### `#[scale_info(skip_type_params(..))]`
112//!
113//! Remove the requirement for the specified type params to implement `TypeInfo`.
114//!
115//! Consider a simple example of a type parameter which is used for associated types, but the type
116//! itself does not carry any type information. Consider this common pattern:
117//!
118//! ```ignore
119//! trait Config {
120//! type Balance;
121//! }
122//!
123//! struct Runtime; // doesn't implement `TypeInfo`
124//!
125//! impl Config for Runtime {
126//! type Balance = u64;
127//! }
128//!
129//! #[allow(unused)]
130//! #[derive(TypeInfo)]
131//! #[scale_info(skip_type_params(T))]
132//! struct A<T: Config> {
133//! balance: T::Balance,
134//! marker: core::marker::PhantomData<T>,
135//! }
136//!
137//! fn assert_type_info<T: eosio_scale_info::TypeInfo + 'static>() {}
138//!
139//! fn main() {
140//! // without the `skip_type_params` attribute this will fail.
141//! assert_type_info::<A<Runtime>>();
142//! }
143//! ```
144//!
145//! By default, the derived `TypeInfo` implementation will add the type of all type parameters to
146//! the `TypeParameter` specification e.g.
147//!
148//! `type_params(vec![TypeParameter::new("T", Some(MetaType::new::<T>()))])`
149//!
150//! In the example above, this will cause a compiler error because `Runtime` is the concrete tyoe
151//! for `T`, which does not satisfy the `TypeInfo` requirement of `MetaType::new::<T>()`.
152//!
153//! Simply adding a `TypeInfo` derive to `Runtime` is one way of solving this, but that could be
154//! misleading (why does it need `TypeInfo` if a value of that type is never encoded?), and can
155//! sometimes require adding `TypeInfo` bounds in other impl blocks.
156//!
157//! The `skip_type_params` attribute solves this, providing an additional "escape hatch" which
158//! prevents the given type parameter's type information being required:
159//!
160//! `type_params(vec![TypeParameter::new("T", None)])`
161//!
162//! The generated type params do not now require `T` to implement `TypeInfo`, so the auto-generated
163//! bound is not added to the generated `TypeInfo` `where` clause.
164//!
165//! #### Combining `bounds` and `skip_type_params`
166//!
167//! These two attributes can complement one another, particularly in the case where using `bounds`
168//! would still require manually adding a `TypeInfo` bound for the type parameter:
169//!
170//! ```ignore
171//! #[derive(TypeInfo)]
172//! #[scale_info(bounds(), skip_type_params(T))]
173//! struct A<T> {
174//! marker: core::marker::PhantomData<T>,
175//! }
176//! ```
177//!
178//! Without `skip_type_params` in the example above, it would require the `TypeInfo` bounds for `T`
179//! to be added manually e.g. `#[scale_info(bounds(T: TypeInfo + 'static))]`. Since the intention of
180//! the empty bounds is to **remove** all type bounds, then the addition of `skip_type_params`
181//! allows this to compile successfully.
182//!
183//! ##### Precedence
184//!
185//! When used independently, both attributes modify the `where` clause of the derived `TypeInfo`
186//! impl. When used together, the predicates supplied in the `bounds` attribute replaces *all*
187//! auto-generated bounds, and `skip_type_params` will have no effect on the resulting `where`
188//! clause.
189//!
190//! **Note:** When using `bounds` without `skip_type_params`, it is therefore required to manually
191//! add a `TypeInfo` bound for any non skipped type parameters. The compiler will let you know.
192//!
193//! #### `#[scale_info(capture_docs = "default|always|never")]`
194//!
195//! Docs for types, fields and variants can all be captured by the `docs` feature being enabled.
196//! This can be overridden using the `capture_docs` attribute:
197//!
198//! `#[scale_info(capture_docs = "default")]` will capture docs iff the `docs` feature is enabled.
199//! This is the default if `capture_docs` is not specified.
200//!
201//! `#[scale_info(capture_docs = "always")]` will capture docs for the annotated type even if the
202//! `docs` feature is *not* enabled.
203//!
204//! `#[scale_info(capture_docs = "never")]` will *not* capture docs for the annotated type even if
205//! the `docs` is enabled.
206//!
207//! This is useful e.g. when compiling metadata into a Wasm blob, and it is desirable to keep the
208//! binary size as small as possible, so the `docs` feature would be disabled. In case the docs for
209//! some types is necessary they could be enabled on a per-type basis with the above attribute.
210//!
211//! #### `#[scale_info(crate = path::to::crate)]`
212//!
213//! Specify a path to the scale-info crate instance to use when referring to the APIs from generated
214//! code. This is normally only applicable when invoking re-exported scale-info derives from a public
215//! macro in a different crate. For example:
216//! ```ignore
217//! use scale_info_reexport::info::TypeInfo;
218//!
219//! #[derive(TypeInfo)]
220//! #[scale_info(crate = scale_info_reexport::info)]
221//! enum TestEnum {
222//! FirstVariant,
223//! SecondVariant,
224//! }
225//! ```
226//!
227//! # Forms
228//!
229//! To bridge between compile-time type information and runtime the
230//! [`MetaForm`](`crate::form::MetaForm`) is used to easily retrieve all
231//! information needed to uniquely identify types.
232//!
233//! The `MetaForm` and its associated `Registry` can be transformed into the
234//! space-efficient form by the [`IntoPortable`](`crate::IntoPortable`) trait; it is
235//! used internally by the [`Registry`](`crate::Registry`) in order to convert
236//! the expanded types into their space-efficient form.
237//!
238//! # Symbols and Namespaces
239//!
240//! To differentiate two types sharing the same name, namespaces are used.
241//! Commonly the namespace is equal to the one where the type has been defined
242//! in. For Rust prelude types such as [`Option`](`std::option::Option`) and
243//! [`Result`](`std::result::Result`) the root namespace (empty namespace) is
244//! used.
245//!
246//! To use this library simply use the [`MetaForm`](`crate::form::MetaForm`)
247//! initially with your own data structures; make them generic over the
248//! [`Form`](`crate::form::Form`) trait just as has been done in this crate with
249//! [`TypeInfo`](`crate::TypeInfo`) in order to get a simple implementation of
250//! [`IntoPortable`](`crate::IntoPortable`). Use a single instance of the
251//! [`Registry`](`crate::Registry`) for compaction and provide this registry
252//! instance upon serialization.
253//!
254//! A usage example can be found in ink! here:
255//! https://github.com/paritytech/ink/blob/master/abi/src/specs.rs
256
257/// Takes a number of types and returns a vector that contains their respective
258/// [`MetaType`](`crate::MetaType`) instances.
259///
260/// This is useful for places that require inputs of iterators over [`MetaType`](`crate::MetaType`)
261/// instances and provide a way out of code bloat in these scenarios.
262///
263/// # Example
264///
265/// ```
266/// # use eosio_scale_info::tuple_meta_type;
267/// assert_eq!(
268/// tuple_meta_type!(i32, [u8; 32], String),
269/// {
270/// use eosio_scale_info::MetaType;
271/// let mut vec = Vec::new();
272/// vec.push(MetaType::new::<i32>());
273/// vec.push(MetaType::new::<[u8; 32]>());
274/// vec.push(MetaType::new::<String>());
275/// vec
276/// }
277/// );
278/// ```
279#[macro_export]
280macro_rules! tuple_meta_type {
281 ( $($ty:ty),* ) => {
282 {
283 $crate::prelude::vec![
284 $(
285 $crate::MetaType::new::<$ty>(),
286 )*
287 ]
288 }
289 }
290}
291
292/// Construct a vector of `TypeParameter`s from pairs of the name and the concrete type.
293///
294/// # Example
295///
296/// ```
297/// # use eosio_scale_info::{named_type_params, MetaType, TypeParameter};
298/// assert_eq!(
299/// named_type_params![(T, u8), (U, u32)],
300/// vec! [
301/// TypeParameter::new("T", Some(MetaType::new::<u8>())),
302/// TypeParameter::new("U", Some(MetaType::new::<u32>())),
303/// ]
304/// );
305/// ```
306#[macro_export]
307macro_rules! named_type_params {
308 ( $(($tp:ty, $ty:ty)),* ) => {
309 {
310 $crate::prelude::vec![
311 $(
312 $crate::TypeParameter::<$crate::form::MetaForm>::new(
313 ::core::stringify!($tp),
314 Some($crate::MetaType::new::<$ty>())
315 ),
316 )*
317 ]
318 }
319 }
320}
321
322/// Construct a vector of [`TypeParameter`] instances with the name of the type parameter,
323/// together with its concrete [`MetaType`].
324#[macro_export]
325macro_rules! type_params {
326 ( $($ty:ty),* ) => {
327 $crate::named_type_params!{ $( ($ty, $ty) ),* }
328 }
329}
330
331pub mod prelude;
332
333pub mod build;
334pub mod form;
335mod impls;
336pub mod interner;
337mod meta_type;
338mod registry;
339mod ty;
340mod utils;
341
342#[cfg(test)]
343mod tests;
344
345#[doc(hidden)]
346pub use scale;
347
348pub use self::{
349 meta_type::MetaType,
350 registry::{
351 IntoPortable,
352 PortableRegistry,
353 Registry,
354 },
355 ty::*,
356};
357
358#[cfg(feature = "derive")]
359pub use eosio_scale_info_derive::TypeInfo;
360
361/// Implementors return their meta type information.
362pub trait TypeInfo {
363 /// The type identifying for which type info is provided.
364 ///
365 /// # Note
366 ///
367 /// This is used to uniquely identify a type via [`core::any::TypeId::of`]. In most cases it
368 /// will just be `Self`, but can be used to unify different types which have the same encoded
369 /// representation e.g. reference types `Box<T>`, `&T` and `&mut T`.
370 type Identity: ?Sized + 'static;
371
372 /// Returns the static type identifier for `Self`.
373 fn type_info() -> Type;
374}
375
376/// Convenience trait for implementors, combining `TypeInfo` and `'static` bounds.
377///
378/// # Note
379///
380/// Currently because of the `'static` constraint on [`std::any::TypeId::of`] (see [`MetaType`]),
381/// `TypeInfo` constraints must also be accompanied by a `'static` bound. This trait is useful to
382/// implementors so only a single constraint is required.
383pub trait StaticTypeInfo: TypeInfo + 'static {}
384
385impl<T> StaticTypeInfo for T where T: TypeInfo + 'static {}
386
387/// Returns the runtime bridge to the types compile-time type information.
388pub fn meta_type<T>() -> MetaType
389where
390 T: ?Sized + TypeInfo + 'static,
391{
392 MetaType::new::<T>()
393}
394
395use lazy_static::lazy_static;
396use std::collections::HashMap;
397use std::sync::Mutex;
398
399#[cfg(feature = "std")]
400lazy_static! {
401 static ref HASHMAP_MUTEX: Mutex<HashMap<String, Type>> = Mutex::new(HashMap::new());
402}
403
404///
405pub fn add_scale_type(ty: Type) {
406 let name = ty.path().segments().to_vec().join("::");
407 if let TypeDef::Primitive(_) = ty.type_def() {
408 return;
409 }
410 HASHMAP_MUTEX.lock().unwrap().insert(name, ty);
411}
412
413///
414pub fn get_scale_type(name: String) -> Option<Type> {
415 HASHMAP_MUTEX.lock().unwrap().get(&name).map(|ty|{
416 ty.clone()
417 })
418}
419
420///
421pub fn get_scale_type_map() -> &'static Mutex<HashMap<String, Type>> {
422 return &HASHMAP_MUTEX;
423}