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}