Derive Macro concordium_contracts_common::Serial
source · #[derive(Serial)]
{
// Attributes available to this derive:
#[concordium]
}
Expand description
Derive the Serial
trait for the type.
If the type is a struct all fields must implement the Serial
trait. If
the type is an enum then all fields of each of the variants must implement
the Serial
trait.
Fields of structs are serialized in the order they appear in the code.
§Enums
Enums can have no more than 65536 variants. They are serialized by using a tag to indicate the variant, and by default they are enumerated in the order they are written in the source code. If the number of variants is less than or to equal 256 then a single byte is used to encode it. Otherwise two bytes are used for the tag, encoded in little endian.
§Specifying the tag byte size using #[concordium(repr(..))]
Optionally, an enum type can be annotated with a #[concordium(repr(x))]
attribute, where x
is either u8
or u16
. This specifies the number of
bytes to use when serializing the tag in little endian.
A type annotated with #[concordium(repr(u8))]
can only have up to 256
variants and #[concordium(repr(u16))]
can have up to 65536 variants.
§Example
Example of an enum which uses two bytes for encoding the tag. Here the
variant A
is tagged using 0u16
and B
is tagged using 1u16
.
#[derive(Serial)]
#[concordium(repr(u16))]
enum MyEnum {
A,
B
}
§Specifying the tag value for a variant using #[concordium(tag = ..)]
For each enum variant the tag can be explicitly set using #[concordium(tag = n)]
where n
is the integer literal to use for the tag.
When using the ‘tag’ attribute it is required to have the
#[concordium(repr(..))]
set as well. The tag must have a value
representable by the type set by #[concordium(repr(..))]
.
Note that SchemaType
currently only supports using a single byte
#([concordium(repr(u8))]
) when using #[concordium(tag = ..)]
.
§Nesting enums with a flat serialization using #[concordium(forward = ...)]
Often it is desired to have a single type representing a parameter or the
events. A general pattern for enums is to nest them, however deriving
serialization for a nested enum introduces an additional tag for the variant
of the top-level enum. The solution is to use the attribute
#[concordium(forward = ...)]
on the variant with a nested enum.
This attribute takes a tag or a list of tags which changes the serialization
to skip the variant tag and deserialization to match the variant with these
tags and forward the deserialization to the nested enum.
#[derive(Serial, Deserial)]
#[concordium(repr(u8))]
enum Event {
SomeEvent(MyEvent),
#[concordium(forward = [42, 43, 44, 45])]
OtherEvent(NestedEvent),
}
For convenience the attribute also supports the values cis2_events
,
cis3_events
and cis4_events
which are unfolded to the list of tags used
for events in CIS-2, CIS-3 and CIS-4 respectively.
#[derive(Serial, Deserial)]
#[concordium(repr(u8))]
enum Event {
SomeEvent(MyEvent),
#[concordium(forward = cis2_events)]
Cis2(Cis2Event),
}
Setting #[concordium(forward = n)]
on a variant will produce an error if:
- The type does not have a
#[concordium(repr(u*))]
attribute. - If any of the forwarded tags
n
cannot be represented by the#[concordium(repr(u*))]
. - Any of the forwarded tags
n
overlap with a tag of another variant. n
contains a predefined set and the value of#[concordium(repr(u*))]
is incompatible.- If the variant does not have exactly one field.
Note that the derive macro does not check forwarded tags matches the tags of the inner type.
§Example
Example of enum specifying the tag of the variant A
to the value 42u8
.
The variant B
is tagged using 1u8
.
#[derive(Serial)]
#[concordium(repr(u8))]
enum MyEnum {
#[concordium(tag = 42)]
A,
B
}
§Generic type bounds
By default a trait bound is added on each generic type for implementing
Serial
. However, if this is not desirable, the default bound can be
replaced by using the bound
attribute on the type and providing the
replacement.
Bounds present in the type declaration will still be present in the implementation, even when a bound is provided:
§Example
#[derive(Serial)]
#[concordium(bound(serial = "A: SomeOtherTrait"))]
struct Foo<A: SomeTrait> {
bar: A,
}
// Derived implementation:
impl <A: SomeTrait> Serial for Foo<A> where A: SomeOtherTrait { .. }
§Collections
Collections (Vec, BTreeMap, BTreeSet) and strings (String, str) are by
default serialized by prepending the number of elements as 4 bytes
little-endian. If this is too much or too little, fields of the above types
can be annotated with size_length
.
The value of this field is the number of bytes that will be used for
encoding the number of elements. Supported values are 1
, 2
, 4
, 8
.
For BTreeMap and BTreeSet the serialize method will serialize values in increasing order of keys.
§Example
#[derive(Serial)]
struct Foo {
#[concordium(size_length = 1)]
bar: BTreeSet<u8>,
}