sbor/schema/
describe.rs

1use crate::*;
2
3/// The `Describe` trait allows a type to describe how to interpret and validate a corresponding SBOR payload.
4///
5/// Each unique interpretation/validation of a type should have its own distinct type in the schema.
6/// Uniqueness of a type in the schema is defined by its `RustTypeId`.
7#[allow(unused_variables)]
8pub trait Describe<C: CustomTypeKind<RustTypeId>> {
9    /// The `TYPE_ID` should give a unique identifier for its SBOR schema type.
10    /// An SBOR schema type capture details about the SBOR payload, how it should be interpreted, validated and displayed.
11    ///
12    /// Conceptually, each type should have a unique id based on:
13    /// * Its SBOR type, structure and child types
14    /// * Any validation that should be applied so that the codec can decode a payload successfully
15    /// * How it should be named or its contents be displayed
16    /// * Any additional data associated with the type which may be added in future (eg i18n or further validation)
17    ///
18    /// For example:
19    /// * An `Array<u32>` and `Array<u64>` are different types because they have different structures
20    /// * Two types named "Content" may be in different namepaces, and wrap different kinds of content, so be different types
21    /// * The tuple `(T1, T2)` is a different type for each `T1` and `T2` because they have different structures
22    /// * Types which are intended to be "transparent" to SBOR such as pointers/smart pointers/etc are equivalent
23    ///   to their wrapper type, so should inherit the `TYPE_ID` of the wrapped type.
24    ///
25    /// Most basic types without additional validation have an associated "Well Known" type, which is intended to save
26    /// room in the schema. Any non-well known types are "Novel" and should be generated for each type.
27    ///
28    /// If needing to generate a novel type id, this can be generated via helper methods on [`RustTypeId`]:
29    /// ```ignore
30    /// impl Describe<C: CustomTypeSchema, T1: Describe<C>> for MyType<T1> {
31    ///     const TYPE_ID: RustTypeId = RustTypeId::complex(stringify!(MyType), &[T1::TYPE_ID]);
32    /// #   fn type_data() -> TypeData<C, RustTypeId> { unreachable!() }
33    /// }
34    /// ```
35    const TYPE_ID: RustTypeId;
36
37    /// Returns the local schema for the given type.
38    ///
39    /// If the `TYPE_ID` is well_known, then this type data must match the corresponding well known type data.
40    fn type_data() -> TypeData<C, RustTypeId>;
41
42    /// For each type referenced in `get_local_type_data`, we need to ensure that the type and all of its own references
43    /// get added to the aggregator.
44    ///
45    /// For direct/simple type dependencies, simply call `aggregator.add_child_type_and_descendents::<D>()`
46    /// for each dependency.
47    ///
48    /// For more complicated type dependencies, where new types are being created (EG where a dependent type
49    /// is being customised/mutated via annotations on the parent type - such as a TypeName override),
50    /// then the algorithm should be:
51    ///
52    /// - Step 1: For each (possibly customised) type dependency needed directly by this type:
53    ///   - Create a new mutated `mutated_type_id` for the underlying type plus its mutation
54    ///   - Use `mutated_type_id` in the relevant place/s in `get_local_type_data`
55    ///   - In `add_all_dependencies` add a line `aggregator.add_child_type(mutated_type_id, mutated_local_type_data)`
56    ///
57    /// - Step 2: For each (base/unmutated) type dependency `D`:
58    ///   - In `add_all_dependencies` add a line `aggregator.add_schema_descendents::<D>()`
59    fn add_all_dependencies(aggregator: &mut TypeAggregator<C>) {}
60}