Derive Macro concordium_std::SchemaType

source ·
#[derive(SchemaType)]
{
    // Attributes available to this derive:
    #[concordium]
}
Expand description

Derive the SchemaType trait for a type with a schema::Type matching the implementation when deriving Serial.

Can be used for enums and structs. If the type is a struct all fields must implement the SchemaType trait. If the type is an enum then all fields of each of the variants must implement the SchemaType trait.

§Specifying the tag value for an enum variant

When deriving Serial, Deserial and DeserialWithState the discriminating tag can be set explicitly using #[concordium(tag = n)] where n is a unsigned integer literal. This require annotating the enum with #[concordium(repr(..))], see Serial for more on this attribute. The current version of the contract schema cannot express tags encoded with more than one byte, meaning only the annotation of #[concordium(repr(u8))] can be used, when deriving the SchemaType.

§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 the schema type for enums with nested enums exposes this. 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 and changes the (de)serialization to hide the nesting. The SchemaType produced is a flatten enum hiding the nested enum. Note that the schema can only be built when the nested type is an enum implementing SchemaType. Incorrect use will not be caught when compiling the contract itself but it will be caught when attempting to build the schema using cargo-concordium.

#[derive(SchemaType)]
#[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(SchemaType)]
#[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.

§Generic type bounds

By default a trait bound is added on each generic type for implementing SchemaType. 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(SchemaType)]
#[concordium(bound(schema_type = "A: SomeOtherTrait"))]
struct Foo<A: SomeTrait> {
    bar: A,
}

// Derived implementation:
impl <A: SomeTrait> SchemaType for Foo<A> where A: SomeOtherTrait { .. }

§Collections

Collections (Vec, BTreeMap, BTreeSet) and strings (String, str) can be annotated with size_length which is the number of bytes used for encoding the number of elements, see derive macro [‘Serial’] for more on this.

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.

§Example

#[derive(SchemaType)]
struct Foo {
    #[concordium(size_length = 1)]
    bar: BTreeSet<u8>,
}

§Transparent

Deriving SchemaType for structs using the newtype design pattern exposes the wrapping struct which is often not desirable. The attribute #[concordium(transparent)] can be added above the struct which changes the implementation of SchemaType to schema type of the field.

The #[concordium(transparent)] attribute can only be used for structs with a single field, and the type of this field must implement SchemaType.

§Example

#[derive(SchemaType)]
#[concordium(transparent)]
struct Foo {
    bar: u32,
}

§Example

The ‘transparent’ attribute will still take account for field attributes such as size_length for collections.

#[derive(SchemaType)]
#[concordium(transparent)]
struct Foo {
    #[concordium(size_length = 1)]
    bar: Vec<u32>,
}