Crates.io | scale-info |
lib.rs | scale-info |
version | 2.11.5 |
source | src |
created_at | 2020-06-12 11:16:47.525527 |
updated_at | 2024-10-24 09:41:51.658588 |
description | Info about SCALE encodable Rust types |
homepage | https://www.parity.io/ |
repository | https://github.com/paritytech/scale-info |
max_upload_size | |
id | 253234 |
size | 178,831 |
A library to describe Rust types, geared towards providing info about the structure of SCALE encodable types.
The definitions provide third party tools (e.g. a UI client) with information about how they are able to decode types agnostic of language.
At its core is the TypeInfo
trait:
pub trait TypeInfo {
type Identity: ?Sized + 'static;
fn type_info() -> Type;
}
Types implementing this trait build up and return a Type
struct:
pub struct Type<T: Form = MetaForm> {
/// The unique path to the type. Can be empty for built-in types
path: Path<T>,
/// The generic type parameters of the type in use. Empty for non generic types
type_params: Vec<T::Type>,
/// The actual type definition
type_def: TypeDef<T>,
}
Types are defined as one of the following variants:
pub enum TypeDef<T: Form = MetaForm> {
/// A composite type (e.g. a struct or a tuple)
Composite(TypeDefComposite<T>),
/// A variant type (e.g. an enum)
Variant(TypeDefVariant<T>),
/// A sequence type with runtime known length.
Sequence(TypeDefSequence<T>),
/// An array type with compile-time known length.
Array(TypeDefArray<T>),
/// A tuple type.
Tuple(TypeDefTuple<T>),
/// A Rust primitive type.
Primitive(TypeDefPrimitive),
}
The following "built-in" types have predefined TypeInfo
definitions:
Primitives: bool
, char
, str
, u8
, u16
, u32
, u64
, u128
, i8
, i16
, i32
, i64
, i128
.
Sequence: Variable size sequence of elements of T
, where T
implements TypeInfo
. e.g. [T]
, &[T]
, &mut [T]
, Vec<T>
Array: Fixed size [T: $n]
for any T
which implements TypeInfo
, where $n
is one of the
predefined sizes.
Tuple: Tuples consisting of up to 10 fields with types implementing TypeInfo
.
There are two kinds of user-defined types: Composite
and Variant
.
Both make use of the Path
and Field
types in their definition:
A fundamental building block to represent user defined types is the Field
struct which defines the Type
of a
field together with its optional name. Builders for the user defined types enforce the invariant that either all
fields have a name (e.g. structs) or all fields are unnamed (e.g. tuples).
The path of a type is a unique sequence of identifiers. Rust types typically construct a path from
the namespace and the identifier e.g. foo::bar::Baz
is converted to the path ["foo", "bar", "Baz"]
.
Composite data types are composed of a set of Fields
.
Structs are represented by a set of named fields, enforced during construction:
struct Foo<T> {
bar: T,
data: u64,
}
impl<T> TypeInfo for Foo<T>
where
T: TypeInfo + 'static,
{
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("Foo", module_path!()))
.type_params(vec![MetaType::new::<T>()])
.composite(Fields::named()
.field(|f| f.ty::<T>().name("bar").type_name("T"))
.field(|f| f.ty::<u64>().name("data").type_name("u64"))
)
}
}
Tuples are represented by a set of unnamed fields, enforced during construction:
struct Foo(u32, bool);
impl TypeInfo for Foo {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("Foo", module_path!()))
.composite(Fields::unnamed()
.field(|f| f.ty::<u32>().type_name("u32"))
.field(|f| f.ty::<bool>().type_name("bool"))
)
}
}
Variant types aka enums or tagged unions are composed of a set of variants. Variants can have unnamed fields, named fields or no fields at all:
enum Foo<T>{
A(T),
B { f: u32 },
C,
}
impl<T> TypeInfo for Foo<T>
where
T: TypeInfo + 'static,
{
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("Foo", module_path!()))
.type_params(vec![MetaType::new::<T>()])
.variant(
Variants::new()
.variant("A", |v| v.fields(Fields::unnamed().field(|f| f.ty::<T>())))
.variant("B", |v| v.fields(Fields::named().field(|f| f.ty::<u32>().name("f").type_name("u32"))))
.variant_unit("C")
)
}
}
If no variants contain fields then the discriminant can be set explicitly, enforced by the builder during construction:
enum Foo {
A,
B,
C = 33,
}
impl TypeInfo for Foo {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("Foo", module_path!()))
.variant(
Variants::new()
.variant("A", |v| v.index(1))
.variant("B", |v| v.index(2))
.variant("C", |v| v.index(33))
)
}
}
Information about types is provided within the so-called type registry (Registry
).
Type definitions are registered there and are associated with unique IDs that the outside
can refer to, providing a lightweight way to decrease overhead instead of using type
identifiers.
All concrete TypeInfo
structures have two forms:
MetaType
) that acts as a bridge to other formsThe IntoPortable
trait must also be implemented in order prepare a type
definition for serialization using an instance of the type registry.
After transformation all type definitions are stored in the type registry. Note that the type registry should be serialized as part of the metadata structure where the registered types are utilized to allow consumers to resolve the types.
The type registry can be encoded as:
parity-scale-codec
).The following optional cargo
features are available:
scale-info-derive
crate.When deriving TypeInfo
for a type with generic compact fields e.g.
#[derive(Encode, TypeInfo)]
struct Foo<S> { #[codec(compact)] a: S }
You may experience the following error when using this generic type without the correct bounds:
error[E0275]: overflow evaluating the requirement `_::_parity_scale_codec::Compact<_>: Decode`
See https://github.com/paritytech/scale-info/issues/65 for more information.
ink!
smart contracts metadata.