Skip to main content

sbor/schema/
schema.rs

1use crate::rust::prelude::*;
2use crate::*;
3
4define_single_versioned!(
5    #[derive(Debug, Clone, PartialEq, Eq, Sbor)]
6    #[sbor(child_types = "S::CustomLocalTypeKind; S::CustomTypeValidation")]
7    pub VersionedSchema(SchemaVersions)<S: CustomSchema> => Schema<S> = SchemaV1::<S>
8);
9
10impl<S: CustomSchema> VersionedSchema<S> {
11    pub fn v1(&self) -> &SchemaV1<S> {
12        self.as_unique_version()
13    }
14
15    pub fn v1_mut(&mut self) -> &mut SchemaV1<S> {
16        self.as_unique_version_mut()
17    }
18}
19
20impl<S: CustomSchema> VersionedSchema<S> {
21    pub fn empty() -> Self {
22        Schema::empty().into()
23    }
24}
25
26impl<S: CustomSchema> Default for VersionedSchema<S> {
27    fn default() -> Self {
28        Self::empty()
29    }
30}
31
32/// A serializable record of the schema of a single type.
33/// Intended for historical backwards compatibility checking of a single type.
34#[derive(Debug, Clone, Sbor)]
35#[sbor(child_types = "S::CustomLocalTypeKind; S::CustomTypeValidation")]
36pub struct SingleTypeSchema<S: CustomSchema> {
37    pub schema: VersionedSchema<S>,
38    pub type_id: LocalTypeId,
39}
40
41impl<S: CustomSchema> SingleTypeSchema<S> {
42    pub fn new(schema: VersionedSchema<S>, type_id: LocalTypeId) -> Self {
43        Self { schema, type_id }
44    }
45
46    pub fn from<T: IntoComparableSchema<Self, S>>(from: T) -> Self {
47        from.into_schema()
48    }
49
50    pub fn for_type<T: Describe<S::CustomAggregatorTypeKind> + ?Sized>() -> Self {
51        generate_single_type_schema::<T, S>()
52    }
53}
54
55/// A serializable record of the schema of a set of named types.
56/// Intended for historical backwards compatibility of a collection
57/// of types in a single schema.
58///
59/// For example, traits, or blueprint interfaces.
60#[derive(Debug, Clone, Sbor)]
61#[sbor(child_types = "S::CustomLocalTypeKind; S::CustomTypeValidation")]
62pub struct TypeCollectionSchema<S: CustomSchema> {
63    pub schema: VersionedSchema<S>,
64    pub type_ids: IndexMap<String, LocalTypeId>,
65}
66
67impl<S: CustomSchema> TypeCollectionSchema<S> {
68    pub fn new(schema: VersionedSchema<S>, type_ids: IndexMap<String, LocalTypeId>) -> Self {
69        Self { schema, type_ids }
70    }
71
72    pub fn from<T: IntoComparableSchema<Self, S>>(from: T) -> Self {
73        from.into_schema()
74    }
75
76    pub fn from_aggregator(aggregator: TypeAggregator<S::CustomAggregatorTypeKind>) -> Self {
77        aggregator.generate_type_collection_schema::<S>()
78    }
79}
80
81/// An array of custom type kinds, and associated extra information which can attach to the type kinds
82#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
83// NB - the generic parameter E isn't embedded in the value model itself - instead:
84// * Via TypeKind, S::CustomLocalTypeKind gets embedded
85// * Via TypeValidation, S::CustomTypeValidation gets embedded
86// So theses are the child types which need to be registered with the sbor macro for it to compile
87#[sbor(child_types = "S::CustomLocalTypeKind; S::CustomTypeValidation")]
88pub struct SchemaV1<S: CustomSchema> {
89    pub type_kinds: Vec<LocalTypeKind<S>>,
90    pub type_metadata: Vec<TypeMetadata>, // TODO: reconsider adding type hash when it's ready!
91    pub type_validations: Vec<TypeValidation<S::CustomTypeValidation>>,
92}
93
94impl<S: CustomSchema> SchemaV1<S> {
95    pub fn empty() -> Self {
96        Self {
97            type_kinds: vec![],
98            type_metadata: vec![],
99            type_validations: vec![],
100        }
101    }
102
103    pub fn resolve_type_kind(&self, type_id: LocalTypeId) -> Option<&LocalTypeKind<S>> {
104        match type_id {
105            LocalTypeId::WellKnown(index) => {
106                S::resolve_well_known_type(index).map(|data| &data.kind)
107            }
108            LocalTypeId::SchemaLocalIndex(index) => self.type_kinds.get(index),
109        }
110    }
111
112    pub fn resolve_type_metadata(&self, type_id: LocalTypeId) -> Option<&TypeMetadata> {
113        match type_id {
114            LocalTypeId::WellKnown(index) => {
115                S::resolve_well_known_type(index).map(|data| &data.metadata)
116            }
117            LocalTypeId::SchemaLocalIndex(index) => self.type_metadata.get(index),
118        }
119    }
120
121    pub fn resolve_matching_tuple_metadata(
122        &self,
123        type_id: LocalTypeId,
124        fields_length: usize,
125    ) -> TupleData<'_> {
126        self.resolve_type_metadata(type_id)
127            .map(|m| m.get_matching_tuple_data(fields_length))
128            .unwrap_or_default()
129    }
130
131    pub fn resolve_matching_enum_metadata(
132        &self,
133        type_id: LocalTypeId,
134        variant_id: u8,
135        fields_length: usize,
136    ) -> EnumVariantData<'_> {
137        self.resolve_type_metadata(type_id)
138            .map(|m| m.get_matching_enum_variant_data(variant_id, fields_length))
139            .unwrap_or_default()
140    }
141
142    pub fn resolve_matching_array_metadata(&self, type_id: LocalTypeId) -> ArrayData<'_> {
143        let Some(TypeKind::Array { element_type }) = self.resolve_type_kind(type_id) else {
144            return ArrayData::default();
145        };
146        ArrayData {
147            array_name: self
148                .resolve_type_metadata(type_id)
149                .and_then(|m| m.get_name()),
150            element_name: self
151                .resolve_type_metadata(*element_type)
152                .and_then(|m| m.get_name()),
153        }
154    }
155
156    pub fn resolve_matching_map_metadata(&self, type_id: LocalTypeId) -> MapData<'_> {
157        let Some(TypeKind::Map {
158            key_type,
159            value_type,
160        }) = self.resolve_type_kind(type_id)
161        else {
162            return MapData::default();
163        };
164        MapData {
165            map_name: self
166                .resolve_type_metadata(type_id)
167                .and_then(|m| m.get_name()),
168            key_name: self
169                .resolve_type_metadata(*key_type)
170                .and_then(|m| m.get_name()),
171            value_name: self
172                .resolve_type_metadata(*value_type)
173                .and_then(|m| m.get_name()),
174        }
175    }
176
177    pub fn resolve_type_name_from_metadata(&self, type_id: LocalTypeId) -> Option<&'_ str> {
178        self.resolve_type_metadata(type_id)
179            .and_then(|m| m.get_name())
180    }
181
182    pub fn resolve_type_validation(
183        &self,
184        type_id: LocalTypeId,
185    ) -> Option<&TypeValidation<S::CustomTypeValidation>> {
186        match type_id {
187            LocalTypeId::WellKnown(index) => {
188                S::resolve_well_known_type(index).map(|data| &data.validation)
189            }
190            LocalTypeId::SchemaLocalIndex(index) => self.type_validations.get(index),
191        }
192    }
193
194    #[allow(clippy::type_complexity)]
195    pub fn resolve_type_data(
196        &self,
197        type_id: LocalTypeId,
198    ) -> Option<(
199        &LocalTypeKind<S>,
200        &TypeMetadata,
201        &TypeValidation<S::CustomTypeValidation>,
202    )> {
203        match type_id {
204            LocalTypeId::WellKnown(index) => {
205                let type_data = S::resolve_well_known_type(index)?;
206                Some((&type_data.kind, &type_data.metadata, &type_data.validation))
207            }
208            LocalTypeId::SchemaLocalIndex(index) => {
209                let type_kind = self.type_kinds.get(index)?;
210                let type_metadata = self.type_metadata.get(index)?;
211                let type_validation = self.type_validations.get(index)?;
212                Some((type_kind, type_metadata, type_validation))
213            }
214        }
215    }
216
217    pub fn validate(&self) -> Result<(), SchemaValidationError> {
218        validate_schema(self)
219    }
220}
221
222impl<S: CustomSchema> Default for SchemaV1<S> {
223    fn default() -> Self {
224        Self::empty()
225    }
226}
227
228#[derive(Debug, Default)]
229pub struct ArrayData<'m> {
230    pub array_name: Option<&'m str>,
231    pub element_name: Option<&'m str>,
232}
233
234#[derive(Debug, Default)]
235pub struct MapData<'m> {
236    pub map_name: Option<&'m str>,
237    pub key_name: Option<&'m str>,
238    pub value_name: Option<&'m str>,
239}