sbor/schema/
type_link.rs

1use const_sha1::ConstSlice;
2use sbor::rust::fmt::Debug;
3use sbor::*;
4
5/// Marker trait for a link between [`TypeKind`]s:
6/// - [`RustTypeId`]: A global identifier for a type (a well known id, or type hash)
7/// - [`LocalTypeId`]: A link in the context of a schema (a well known id, or a local type index)
8pub trait SchemaTypeLink: Debug + Clone + PartialEq + Eq + From<WellKnownTypeId> {}
9
10/// A newer alias for `RustTypeId`.
11pub type AggregatorTypeId = RustTypeId;
12
13/// This is a compile-time identifier for a given type, used by the type aggregator
14/// to uniquely identify a type.
15#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Sbor)]
16pub enum RustTypeId {
17    /// This takes a well_known type index.
18    WellKnown(WellKnownTypeId),
19    /// The global type hash of a type - used for types which aren't well known.
20    Novel(TypeHash),
21}
22
23impl From<WellKnownTypeId> for RustTypeId {
24    fn from(value: WellKnownTypeId) -> Self {
25        RustTypeId::WellKnown(value)
26    }
27}
28
29impl SchemaTypeLink for RustTypeId {}
30
31pub type TypeHash = [u8; 20];
32
33impl RustTypeId {
34    pub const fn novel(name: &str, dependencies: &[RustTypeId]) -> Self {
35        generate_type_hash(&[name], &[], dependencies)
36    }
37
38    pub const fn novel_with_code(name: &str, dependencies: &[RustTypeId], code: &[u8]) -> Self {
39        generate_type_hash(&[name], &[("code", code)], dependencies)
40    }
41
42    pub const fn novel_validated(
43        name: &str,
44        dependencies: &[RustTypeId],
45        validations: &[(&str, &[u8])],
46    ) -> Self {
47        generate_type_hash(&[name], validations, dependencies)
48    }
49
50    pub const fn to_const_slice(&self) -> ConstSlice {
51        match &self {
52            RustTypeId::WellKnown(x) => ConstSlice::from_slice(&x.0.to_be_bytes()),
53            RustTypeId::Novel(hash) => ConstSlice::from_slice(hash),
54        }
55    }
56}
57
58const fn generate_type_hash(
59    names: &[&str],
60    type_data: &[(&str, &[u8])],
61    dependencies: &[RustTypeId],
62) -> RustTypeId {
63    let buffer = const_sha1::ConstSlice::new();
64
65    // Const looping isn't allowed - but we can use recursion instead
66    let buffer = capture_names(buffer, 0, names);
67    let buffer = capture_type_data(buffer, 0, type_data);
68    let buffer = capture_dependent_type_ids(buffer, 0, dependencies);
69
70    RustTypeId::Novel(const_sha1::sha1(buffer.as_slice()).as_bytes())
71}
72
73const fn capture_names(
74    buffer: const_sha1::ConstSlice,
75    next: usize,
76    names: &[&str],
77) -> const_sha1::ConstSlice {
78    if next == names.len() {
79        return buffer;
80    }
81    let buffer = buffer.push_slice(names[next].as_bytes());
82    capture_names(buffer, next + 1, names)
83}
84
85const fn capture_type_data(
86    buffer: const_sha1::ConstSlice,
87    next: usize,
88    type_data: &[(&str, &[u8])],
89) -> const_sha1::ConstSlice {
90    if next == type_data.len() {
91        return buffer;
92    }
93    let buffer = buffer.push_slice(type_data[next].0.as_bytes());
94    let buffer = buffer.push_slice(type_data[next].1);
95    capture_type_data(buffer, next + 1, type_data)
96}
97
98const fn capture_dependent_type_ids(
99    buffer: const_sha1::ConstSlice,
100    next: usize,
101    dependencies: &[RustTypeId],
102) -> const_sha1::ConstSlice {
103    if next == dependencies.len() {
104        return buffer;
105    }
106    let buffer = buffer.push_other(dependencies[next].to_const_slice());
107    capture_dependent_type_ids(buffer, next + 1, dependencies)
108}
109
110/// The TypeId which is local to a given [`Schema`].
111/// This is the [`SchemaTypeLink`] used in a linearized [`Schema`] to link [`TypeKind`]s.
112#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Sbor)]
113pub enum LocalTypeId {
114    /// This takes a well_known type index
115    WellKnown(WellKnownTypeId),
116    /// For non-simple types
117    SchemaLocalIndex(usize),
118}
119
120impl From<WellKnownTypeId> for LocalTypeId {
121    fn from(value: WellKnownTypeId) -> Self {
122        LocalTypeId::WellKnown(value)
123    }
124}
125
126impl SchemaTypeLink for LocalTypeId {}
127
128impl LocalTypeId {
129    pub fn any() -> Self {
130        Self::WellKnown(basic_well_known_types::ANY_TYPE.into())
131    }
132}
133
134#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Sbor)]
135#[sbor(transparent)]
136pub struct WellKnownTypeId(u8);
137
138impl WellKnownTypeId {
139    pub const fn of(x: u8) -> Self {
140        Self(x as u8)
141    }
142
143    pub const fn as_index(&self) -> usize {
144        self.0 as usize
145    }
146}