sbor/schema/schema_comparison/
comparable_schema.rs

1use super::*;
2
3/// A list of named comparable schemas, intended to capture various versions
4/// of the same schema over time.
5pub struct NamedSchemaVersions<S: CustomSchema, C: ComparableSchema<S>> {
6    ordered_versions: IndexMap<String, C>,
7    custom_schema: PhantomData<S>,
8}
9
10impl<S: CustomSchema, C: ComparableSchema<S>> Default for NamedSchemaVersions<S, C> {
11    fn default() -> Self {
12        Self::new()
13    }
14}
15
16impl<S: CustomSchema, C: ComparableSchema<S>> NamedSchemaVersions<S, C> {
17    pub fn new() -> Self {
18        Self {
19            ordered_versions: Default::default(),
20            custom_schema: Default::default(),
21        }
22    }
23
24    pub fn from<F: IntoIterator<Item = (K, V)>, K: AsRef<str>, V: IntoComparableSchema<C, S>>(
25        from: F,
26    ) -> Self {
27        Self {
28            ordered_versions: from
29                .into_iter()
30                .map(|(name, version)| (name.as_ref().to_string(), version.into_schema()))
31                .collect(),
32            custom_schema: Default::default(),
33        }
34    }
35
36    pub fn register_version(
37        mut self,
38        name: impl AsRef<str>,
39        version: impl IntoComparableSchema<C, S>,
40    ) -> Self {
41        self.ordered_versions
42            .insert(name.as_ref().to_string(), version.into_schema());
43        self
44    }
45
46    pub fn get_versions(&self) -> &IndexMap<String, C> {
47        &self.ordered_versions
48    }
49}
50
51/// Marker trait for [`SingleTypeSchema`] and [`TypeCollectionSchema`] which
52/// includes named pointers to types, and can be used for comparisons of
53/// different versions of the same schema.
54pub trait ComparableSchema<S: CustomSchema>: Clone + VecSbor<S::DefaultCustomExtension> {
55    fn encode_to_bytes(&self) -> Vec<u8> {
56        vec_encode::<S::DefaultCustomExtension, Self>(self, BASIC_SBOR_V1_MAX_DEPTH).unwrap()
57    }
58
59    fn encode_to_hex(&self) -> String {
60        hex::encode(self.encode_to_bytes())
61    }
62
63    fn decode_from_bytes(bytes: &[u8]) -> Self {
64        vec_decode_with_nice_error::<S::DefaultCustomExtension, Self>(
65            bytes,
66            BASIC_SBOR_V1_MAX_DEPTH,
67        )
68        .unwrap_or_else(|err| {
69            panic!(
70                "Could not SBOR decode bytes into {} with {}: {:?}",
71                core::any::type_name::<Self>(),
72                core::any::type_name::<S::DefaultCustomExtension>(),
73                err,
74            )
75        })
76    }
77
78    fn decode_from_hex(hex: &str) -> Self {
79        let bytes = hex::decode(hex)
80            .unwrap_or_else(|err| panic!("Provided string was not valid hex: {err}"));
81        Self::decode_from_bytes(&bytes)
82    }
83
84    fn compare_with<'s>(
85        &'s self,
86        compared: &'s Self,
87        settings: &SchemaComparisonSettings,
88    ) -> SchemaComparisonResult<'s, S>;
89}
90
91impl<S: CustomSchema> ComparableSchema<S> for SingleTypeSchema<S> {
92    fn compare_with<'s>(
93        &'s self,
94        compared: &'s Self,
95        settings: &SchemaComparisonSettings,
96    ) -> SchemaComparisonResult<'s, S> {
97        SchemaComparisonKernel::new(
98            self.schema.as_unique_version(),
99            compared.schema.as_unique_version(),
100            settings,
101        )
102        .compare_using_fixed_type_roots(&[ComparisonTypeRoot {
103            name: "root".to_string(),
104            base_type_id: self.type_id,
105            compared_type_id: compared.type_id,
106        }])
107    }
108}
109
110impl<S: CustomSchema> ComparableSchema<S> for TypeCollectionSchema<S> {
111    fn compare_with<'s>(
112        &'s self,
113        compared: &'s Self,
114        settings: &SchemaComparisonSettings,
115    ) -> SchemaComparisonResult<'s, S> {
116        SchemaComparisonKernel::new(
117            self.schema.as_unique_version(),
118            compared.schema.as_unique_version(),
119            settings,
120        )
121        .compare_using_named_type_roots(&self.type_ids, &compared.type_ids)
122    }
123}
124
125pub trait IntoComparableSchema<C: ComparableSchema<S>, S: CustomSchema> {
126    #[allow(clippy::wrong_self_convention)]
127    fn into_schema(&self) -> C;
128}
129
130impl<S: CustomSchema> IntoComparableSchema<Self, S> for SingleTypeSchema<S> {
131    fn into_schema(&self) -> Self {
132        self.clone()
133    }
134}
135
136impl<S: CustomSchema> IntoComparableSchema<Self, S> for TypeCollectionSchema<S> {
137    fn into_schema(&self) -> Self {
138        self.clone()
139    }
140}
141
142impl<C: ComparableSchema<S>, S: CustomSchema, T: IntoComparableSchema<C, S> + ?Sized>
143    IntoComparableSchema<C, S> for &T
144{
145    fn into_schema(&self) -> C {
146        <T as IntoComparableSchema<C, S>>::into_schema(*self)
147    }
148}
149
150impl<C: ComparableSchema<S>, S: CustomSchema> IntoComparableSchema<C, S> for [u8] {
151    fn into_schema(&self) -> C {
152        C::decode_from_bytes(self)
153    }
154}
155
156impl<C: ComparableSchema<S>, S: CustomSchema, const N: usize> IntoComparableSchema<C, S>
157    for [u8; N]
158{
159    fn into_schema(&self) -> C {
160        C::decode_from_bytes(self.as_slice())
161    }
162}
163
164impl<C: ComparableSchema<S>, S: CustomSchema> IntoComparableSchema<C, S> for Vec<u8> {
165    fn into_schema(&self) -> C {
166        C::decode_from_bytes(self)
167    }
168}
169
170impl<C: ComparableSchema<S>, S: CustomSchema> IntoComparableSchema<C, S> for String {
171    fn into_schema(&self) -> C {
172        C::decode_from_hex(self)
173    }
174}
175
176impl<C: ComparableSchema<S>, S: CustomSchema> IntoComparableSchema<C, S> for str {
177    fn into_schema(&self) -> C {
178        C::decode_from_hex(self)
179    }
180}