hydrate_schema/schema_cache/
mod.rs

1//! This package handles loading/saving the schema cache. The schema cache is a persistent,
2//! immutable record of all schemas that have ever existed. This assures that given a fingerprint
3//! and data, we can always load the data that was serialized, even if the schema or code have
4//! been changed.
5
6use crate::{
7    HashMap, Schema, SchemaDefRecordFieldMarkup, SchemaDefRecordMarkup, SchemaDynamicArray,
8    SchemaEnum, SchemaEnumSymbol, SchemaFingerprint, SchemaMap, SchemaNamedType, SchemaRecord,
9    SchemaRecordField, SchemaStaticArray,
10};
11use serde::{Deserialize, Serialize};
12use uuid::Uuid;
13
14#[derive(Debug, Serialize, Deserialize)]
15struct CachedSchemaStaticArray {
16    item_type: Box<CachedSchema>,
17    length: usize,
18}
19
20impl CachedSchemaStaticArray {
21    fn new_from_schema(schema: &SchemaStaticArray) -> Self {
22        CachedSchemaStaticArray {
23            item_type: Box::new(CachedSchema::new_from_schema(schema.item_type())),
24            length: schema.length,
25        }
26    }
27
28    fn to_schema(self) -> SchemaStaticArray {
29        SchemaStaticArray::new(Box::new(self.item_type.to_schema()), self.length)
30    }
31}
32
33#[derive(Debug, Serialize, Deserialize)]
34struct CachedSchemaDynamicArray {
35    item_type: Box<CachedSchema>,
36}
37
38impl CachedSchemaDynamicArray {
39    fn new_from_schema(schema: &SchemaDynamicArray) -> Self {
40        CachedSchemaDynamicArray {
41            item_type: Box::new(CachedSchema::new_from_schema(schema.item_type())),
42        }
43    }
44
45    fn to_schema(self) -> SchemaDynamicArray {
46        SchemaDynamicArray::new(Box::new(self.item_type.to_schema()))
47    }
48}
49
50#[derive(Debug, Serialize, Deserialize)]
51struct CachedSchemaMap {
52    key_type: Box<CachedSchema>,
53    value_type: Box<CachedSchema>,
54}
55
56impl CachedSchemaMap {
57    fn new_from_schema(schema: &SchemaMap) -> Self {
58        CachedSchemaMap {
59            key_type: Box::new(CachedSchema::new_from_schema(schema.key_type())),
60            value_type: Box::new(CachedSchema::new_from_schema(schema.value_type())),
61        }
62    }
63
64    fn to_schema(self) -> SchemaMap {
65        SchemaMap::new(
66            Box::new(self.key_type.to_schema()),
67            Box::new(self.value_type.to_schema()),
68        )
69    }
70}
71
72#[derive(Debug, Serialize, Deserialize)]
73struct CachedSchemaRecordField {
74    name: String,
75    field_uuid: Uuid,
76    #[serde(skip_serializing_if = "Vec::is_empty", default)]
77    aliases: Vec<String>,
78    field_schema: Box<CachedSchema>,
79}
80
81impl CachedSchemaRecordField {
82    fn new_from_schema(schema: &SchemaRecordField) -> Self {
83        CachedSchemaRecordField {
84            name: schema.name().to_string(),
85            field_uuid: schema.field_uuid(),
86            aliases: schema.aliases().iter().cloned().collect(),
87            field_schema: Box::new(CachedSchema::new_from_schema(schema.field_schema())),
88        }
89    }
90
91    fn to_schema(self) -> SchemaRecordField {
92        SchemaRecordField::new(
93            self.name,
94            self.field_uuid,
95            self.aliases.into_boxed_slice(),
96            self.field_schema.to_schema(),
97            SchemaDefRecordFieldMarkup::default(),
98        )
99    }
100}
101
102#[derive(Debug, Serialize, Deserialize)]
103pub struct CachedSchemaRecord {
104    name: String,
105    type_uuid: Uuid,
106    fingerprint: Uuid,
107    #[serde(skip_serializing_if = "Vec::is_empty", default)]
108    aliases: Vec<String>,
109    fields: Vec<CachedSchemaRecordField>,
110}
111
112impl CachedSchemaRecord {
113    fn new_from_schema(schema: &SchemaRecord) -> Self {
114        let mut fields = Vec::with_capacity(schema.fields().len());
115        for field in schema.fields() {
116            fields.push(CachedSchemaRecordField::new_from_schema(field));
117        }
118
119        CachedSchemaRecord {
120            name: schema.name().to_string(),
121            type_uuid: schema.type_uuid(),
122            fingerprint: schema.fingerprint().as_uuid(),
123            aliases: schema.aliases().iter().cloned().collect(),
124            fields,
125        }
126    }
127
128    fn to_schema(self) -> SchemaRecord {
129        let mut fields = Vec::with_capacity(self.fields.len());
130        for field in self.fields {
131            fields.push(field.to_schema());
132        }
133
134        SchemaRecord::new(
135            self.name,
136            self.type_uuid,
137            SchemaFingerprint(self.fingerprint.as_u128()),
138            self.aliases.into_boxed_slice(),
139            fields,
140            SchemaDefRecordMarkup::default(),
141        )
142    }
143}
144
145#[derive(Debug, Serialize, Deserialize)]
146struct CachedSchemaEnumSymbol {
147    name: String,
148    symbol_uuid: Uuid,
149    #[serde(skip_serializing_if = "Vec::is_empty", default)]
150    aliases: Vec<String>,
151    //value: i32,
152}
153
154impl CachedSchemaEnumSymbol {
155    fn new_from_schema(schema: &SchemaEnumSymbol) -> Self {
156        CachedSchemaEnumSymbol {
157            name: schema.name().to_string(),
158            symbol_uuid: schema.symbol_uuid(),
159            aliases: schema.aliases().iter().cloned().collect(),
160            //value: schema.value(),
161        }
162    }
163
164    fn to_schema(self) -> SchemaEnumSymbol {
165        SchemaEnumSymbol::new(
166            self.name,
167            self.symbol_uuid,
168            self.aliases.into_boxed_slice(), /*, self.value*/
169        )
170    }
171}
172
173#[derive(Debug, Serialize, Deserialize)]
174pub struct CachedSchemaEnum {
175    name: String,
176    type_uuid: Uuid,
177    fingerprint: Uuid,
178    #[serde(skip_serializing_if = "Vec::is_empty", default)]
179    aliases: Vec<String>,
180    symbols: Vec<CachedSchemaEnumSymbol>,
181}
182
183impl CachedSchemaEnum {
184    fn new_from_schema(schema: &SchemaEnum) -> Self {
185        let mut symbols = Vec::with_capacity(schema.symbols().len());
186        for field in schema.symbols() {
187            symbols.push(CachedSchemaEnumSymbol::new_from_schema(field));
188        }
189
190        CachedSchemaEnum {
191            name: schema.name().to_string(),
192            type_uuid: schema.type_uuid(),
193            fingerprint: schema.fingerprint().as_uuid(),
194            aliases: schema.aliases().iter().cloned().collect(),
195            symbols,
196        }
197    }
198
199    fn to_schema(self) -> SchemaEnum {
200        let mut symbols = Vec::with_capacity(self.symbols.len());
201        for symbol in self.symbols {
202            symbols.push(symbol.to_schema());
203        }
204
205        SchemaEnum::new(
206            self.name,
207            self.type_uuid,
208            SchemaFingerprint(self.fingerprint.as_u128()),
209            self.aliases.into_boxed_slice(),
210            symbols.into_boxed_slice(),
211        )
212    }
213}
214
215#[derive(Debug, Serialize, Deserialize)]
216pub enum CachedSchemaNamedType {
217    Record(CachedSchemaRecord),
218    Enum(CachedSchemaEnum),
219}
220
221impl CachedSchemaNamedType {
222    pub fn fingerprint(&self) -> Uuid {
223        match self {
224            CachedSchemaNamedType::Record(x) => x.fingerprint,
225            CachedSchemaNamedType::Enum(x) => x.fingerprint,
226        }
227    }
228
229    pub fn new_from_schema(schema: &SchemaNamedType) -> CachedSchemaNamedType {
230        match schema {
231            SchemaNamedType::Record(x) => {
232                CachedSchemaNamedType::Record(CachedSchemaRecord::new_from_schema(x))
233            }
234            SchemaNamedType::Enum(x) => {
235                CachedSchemaNamedType::Enum(CachedSchemaEnum::new_from_schema(x))
236            }
237        }
238    }
239
240    pub fn to_schema(self) -> SchemaNamedType {
241        match self {
242            CachedSchemaNamedType::Record(x) => SchemaNamedType::Record(x.to_schema()),
243            CachedSchemaNamedType::Enum(x) => SchemaNamedType::Enum(x.to_schema()),
244        }
245    }
246}
247
248#[derive(Debug, Serialize, Deserialize)]
249enum CachedSchema {
250    /// Marks the field as possible to be null
251    Nullable(Box<CachedSchema>),
252    Boolean,
253    I32,
254    I64,
255    U32,
256    U64,
257    F32,
258    F64,
259    /// Variable amount of bytes stored within the asset, intended to be relatively small
260    Bytes,
261    /// Variable-length UTF-8 String
262    String,
263    /// Fixed-size array of values
264    StaticArray(CachedSchemaStaticArray),
265    DynamicArray(CachedSchemaDynamicArray),
266    Map(CachedSchemaMap),
267    //RecordRef(CachedSchemaRefConstraint),
268    AssetRef(Uuid),
269    /// Named type, it could be an enum, record, etc.
270    Record(Uuid),
271    Enum(Uuid),
272}
273
274impl CachedSchema {
275    fn new_from_schema(schema: &Schema) -> CachedSchema {
276        match schema {
277            Schema::Nullable(inner_schema) => {
278                CachedSchema::Nullable(Box::new(CachedSchema::new_from_schema(&*inner_schema)))
279            }
280            Schema::Boolean => CachedSchema::Boolean,
281            Schema::I32 => CachedSchema::I32,
282            Schema::I64 => CachedSchema::I64,
283            Schema::U32 => CachedSchema::U32,
284            Schema::U64 => CachedSchema::U64,
285            Schema::F32 => CachedSchema::F32,
286            Schema::F64 => CachedSchema::F64,
287            Schema::Bytes => CachedSchema::Bytes,
288            Schema::String => CachedSchema::String,
289            Schema::StaticArray(x) => {
290                CachedSchema::StaticArray(CachedSchemaStaticArray::new_from_schema(x))
291            }
292            Schema::DynamicArray(x) => {
293                CachedSchema::DynamicArray(CachedSchemaDynamicArray::new_from_schema(x))
294            }
295            Schema::Map(x) => CachedSchema::Map(CachedSchemaMap::new_from_schema(x)),
296            //Schema::RecordRef(x) => CachedSchemaStaticArray::new_from_schema(x),
297            Schema::AssetRef(x) => CachedSchema::AssetRef(x.as_uuid()),
298            Schema::Record(x) => CachedSchema::Record(x.as_uuid()),
299            Schema::Enum(x) => CachedSchema::Enum(x.as_uuid()),
300        }
301    }
302
303    fn to_schema(self) -> Schema {
304        match self {
305            CachedSchema::Nullable(x) => Schema::Nullable(Box::new(x.to_schema())),
306            CachedSchema::Boolean => Schema::Boolean,
307            CachedSchema::I32 => Schema::I32,
308            CachedSchema::I64 => Schema::I64,
309            CachedSchema::U32 => Schema::U32,
310            CachedSchema::U64 => Schema::U64,
311            CachedSchema::F32 => Schema::F32,
312            CachedSchema::F64 => Schema::F64,
313            CachedSchema::Bytes => Schema::Bytes,
314            CachedSchema::String => Schema::String,
315            CachedSchema::StaticArray(x) => Schema::StaticArray(x.to_schema()),
316            CachedSchema::DynamicArray(x) => Schema::DynamicArray(x.to_schema()),
317            CachedSchema::Map(x) => Schema::Map(x.to_schema()),
318            CachedSchema::AssetRef(x) => Schema::AssetRef(SchemaFingerprint(x.as_u128())),
319            CachedSchema::Record(x) => Schema::Record(SchemaFingerprint(x.as_u128())),
320            CachedSchema::Enum(x) => Schema::Enum(SchemaFingerprint(x.as_u128())),
321        }
322    }
323}
324
325/// Handles saving and loading a schema cache, an immutable repository of all schemas that have ever
326/// existed.
327#[derive(Debug, Serialize, Deserialize, Default)]
328pub struct SchemaCacheSingleFile {
329    cached_schemas: Vec<CachedSchemaNamedType>,
330}
331
332impl SchemaCacheSingleFile {
333    pub fn store_string(schemas: &HashMap<SchemaFingerprint, SchemaNamedType>) -> String {
334        let mut cached_schemas: Vec<CachedSchemaNamedType> = Default::default();
335
336        for (_, schema) in schemas {
337            cached_schemas.push(CachedSchemaNamedType::new_from_schema(schema));
338        }
339
340        cached_schemas.sort_by_key(|x| x.fingerprint());
341
342        let cache = SchemaCacheSingleFile { cached_schemas };
343
344        profiling::scope!("serde_json::to_string_pretty");
345        serde_json::to_string_pretty(&cache).unwrap()
346    }
347
348    pub fn load_string(cache: &str) -> Vec<SchemaNamedType> {
349        let cache: SchemaCacheSingleFile = {
350            profiling::scope!("serde_json::from_str");
351            serde_json::from_str(cache).unwrap()
352        };
353        cache
354            .cached_schemas
355            .into_iter()
356            .map(|x| x.to_schema())
357            .collect()
358    }
359}