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