termite_dmg/schema/
mod.rs

1use crate::{data_model, DefaultType};
2use jzon::JsonValue;
3use std::{
4    collections::{HashMap, HashSet},
5    fmt,
6};
7
8impl data_model::DataModel {
9    /// Creates a JSON schema from the data model
10    ///
11    /// # Parameters
12    ///
13    /// id: The name of the type to export as a schema
14    ///
15    /// schema_id: The JSON Schema id to put in the json object
16    pub fn export_schema(&self, id: &str, schema_id: &str) -> Result<JsonValue, Error> {
17        // Convert the data model to a map
18        let data_types = HashMap::<String, data_model::DataType>::from_iter(
19            self.data_types
20                .iter()
21                .map(|data_type| (data_type.name.clone(), data_type.clone())),
22        );
23
24        // Find the main type
25        let main_type = data_types.get(id).ok_or(Error {
26            location: "".to_string(),
27            error: ErrorCore::UnknownID(id.to_string()),
28        })?;
29
30        // Construct all of the definitions
31        let mut defs = jzon::object::Object::new();
32        let mut dependencies = HashSet::new();
33        let mut implemented_types = HashSet::new();
34        dependencies.insert(main_type.name.clone());
35        while !dependencies.is_empty() {
36            // Get the type to implement
37            let implement_type = if let Some(value) = dependencies
38                .iter()
39                .filter(|value| !implemented_types.contains(*value))
40                .next()
41            {
42                value.clone()
43            } else {
44                break;
45            };
46
47            // Construct the type schema
48            let type_schema = data_types.get(&implement_type).unwrap().export_schema(
49                &data_types,
50                &mut dependencies,
51                &self.macros,
52            )?;
53
54            // Add it to the definitions
55            defs.insert(&implement_type, JsonValue::Object(type_schema));
56
57            // Add to the implemented types
58            implemented_types.insert(implement_type);
59        }
60
61        // Construct the main type schema
62        let schema = jzon::object! {
63            "$comment": format!("This schema is generated using Termite Data Model Generator to be compatible with a generated data model using version {}.{}", std::env!("CARGO_PKG_VERSION_MAJOR"), std::env!("CARGO_PKG_VERSION_MINOR")),
64            "$schema": "https://json-schema.org/draft/2020-12/schema",
65            "$id": schema_id.to_string(),
66            "$ref": id.to_string(),
67            "$defs": JsonValue::Object(defs),
68        };
69
70        return Ok(schema);
71    }
72}
73
74impl data_model::DataType {
75    /// Creates a JSON schema from the data type
76    ///
77    /// # Parameters
78    ///
79    /// custom_types: The map of all the custom types, used to check if a type is builtin or not
80    ///
81    /// dependencies: A set to add all dependencies of this data type to
82    ///
83    /// macros: A map of all macros to expand default values
84    pub fn export_schema(
85        &self,
86        custom_types: &HashMap<String, data_model::DataType>,
87        dependencies: &mut HashSet<String>,
88        macros: &HashMap<String, data_model::SerializationModel>,
89    ) -> Result<jzon::object::Object, Error> {
90        // Convert the data to a schema
91        let mut schema = self
92            .data
93            .export_schema(custom_types, dependencies, macros)?;
94
95        // Add the id
96        schema.insert("$id", JsonValue::String(self.name.clone()));
97
98        // Add the description
99        if let Some(description) = &self.description {
100            schema.insert("description", JsonValue::String(description.clone()));
101        }
102
103        return Ok(schema);
104    }
105
106    /// Converts a serialization model value into a JSON value of the for of this type
107    ///
108    /// # Parameters
109    ///
110    /// value: The serialization model to convert
111    ///
112    /// custom_types: All the custom types in the schema
113    pub fn schema_value(
114        &self,
115        value: &data_model::SerializationModel,
116        custom_types: &HashMap<String, data_model::DataType>,
117    ) -> Result<JsonValue, Error> {
118        return self.data.schema_value(value, custom_types);
119    }
120}
121
122impl data_model::DataTypeData {
123    /// Creates a JSON schema from the data type data
124    ///
125    /// # Parameters
126    ///
127    /// custom_types: The map of all the custom types, used to check if a type is builtin or not
128    ///
129    /// dependencies: A set to add all dependencies of this data type data to
130    ///
131    /// macros: A map of all macros to expand default values
132    pub fn export_schema(
133        &self,
134        custom_types: &HashMap<String, data_model::DataType>,
135        dependencies: &mut HashSet<String>,
136        macros: &HashMap<String, data_model::SerializationModel>,
137    ) -> Result<jzon::object::Object, Error> {
138        return match self {
139            data_model::DataTypeData::Struct(data) => {
140                data.export_schema(custom_types, dependencies, macros)
141            }
142            data_model::DataTypeData::Array(data) => data.export_schema(custom_types, dependencies),
143            data_model::DataTypeData::Variant(data) => {
144                data.export_schema(custom_types, dependencies)
145            }
146            data_model::DataTypeData::Enum(data) => data.export_schema(custom_types, dependencies),
147            data_model::DataTypeData::ConstrainedType(data) => {
148                data.export_schema(custom_types, dependencies)
149            }
150        };
151    }
152
153    /// Converts a serialization model value into a JSON value of the for of this type
154    ///
155    /// # Parameters
156    ///
157    /// value: The serialization model to convert
158    ///
159    /// custom_types: All the custom types in the schema
160    pub fn schema_value(
161        &self,
162        value: &data_model::SerializationModel,
163        custom_types: &HashMap<String, data_model::DataType>,
164    ) -> Result<JsonValue, Error> {
165        return match self {
166            data_model::DataTypeData::Struct(data) => data.schema_value(value, custom_types),
167            data_model::DataTypeData::Array(data) => data.schema_value(value, custom_types),
168            data_model::DataTypeData::Variant(data) => data.schema_value(value, custom_types),
169            data_model::DataTypeData::Enum(data) => data.schema_value(value, custom_types),
170            data_model::DataTypeData::ConstrainedType(data) => {
171                data.schema_value(value, custom_types)
172            }
173        };
174    }
175}
176
177impl data_model::Struct {
178    /// Creates a JSON schema from the struct
179    ///
180    /// # Parameters
181    ///
182    /// custom_types: The map of all the custom types, used to check if a type is builtin or not
183    ///
184    /// dependencies: A set to add all dependencies of this struct to
185    ///
186    /// macros: A map of all macros to expand default values
187    pub fn export_schema(
188        &self,
189        custom_types: &HashMap<String, data_model::DataType>,
190        dependencies: &mut HashSet<String>,
191        macros: &HashMap<String, data_model::SerializationModel>,
192    ) -> Result<jzon::object::Object, Error> {
193        // Setup the required list
194        let mut required = vec![];
195
196        // Setup the properties
197        let mut properties = jzon::object::Object::new();
198
199        // Add all the fields
200        for field in self.fields.iter() {
201            // Add the type to dependencies
202            let ref_keyword = is_schema_type(&field.data_type, custom_types, dependencies)
203                .or_else(|error| {
204                    return Err(error.add_field(&field.name));
205                })?;
206
207            // Construct the schema
208            let mut field_schema = jzon::object::Object::new();
209            field_schema.insert(ref_keyword, JsonValue::String(field.data_type.clone()));
210            if let Some(description) = &field.description {
211                field_schema.insert("description", JsonValue::String(description.clone()));
212            }
213            match &field.default {
214                DefaultType::Optional => (),
215                DefaultType::Required => required.push(JsonValue::String(field.name.clone())),
216                DefaultType::Default(value) => {
217                    field_schema.insert(
218                        "default",
219                        to_json(
220                            &data_model::expand_macros(value, macros, &mut HashSet::new())?,
221                            &field.data_type,
222                            custom_types,
223                        )?,
224                    );
225                }
226            }
227
228            // Add to the properties
229            properties.insert(&field.name, JsonValue::Object(field_schema));
230        }
231
232        // Setup the schema
233        let mut schema = jzon::object::Object::new();
234        schema.insert("properties", JsonValue::Object(properties));
235        schema.insert("required", JsonValue::Array(required));
236        schema.insert("type", JsonValue::String("object".to_string()));
237        schema.insert(
238            "$comment",
239            JsonValue::String("A struct which contains a set of fields".to_string()),
240        );
241
242        // Get the inheritence
243        if let Some(inherit) = &self.inherit {
244            match custom_types.get(inherit) {
245                Some(value) => match &value.data {
246                    data_model::DataTypeData::Struct(_) => {
247                        dependencies.insert(inherit.clone());
248                        schema.insert("$ref", JsonValue::String(inherit.clone()));
249                    }
250                    _ => {
251                        return Err(Error {
252                            location: "".to_string(),
253                            error: ErrorCore::EnheritStruct(inherit.clone()),
254                        })
255                    }
256                },
257                None => {
258                    return Err(Error {
259                        location: "".to_string(),
260                        error: ErrorCore::UnknownID(inherit.clone()),
261                    });
262                }
263            }
264        }
265
266        return Ok(schema);
267    }
268
269    /// Converts a serialization model value into a JSON value of the for of this type
270    ///
271    /// # Parameters
272    ///
273    /// value: The serialization model to convert
274    ///
275    /// custom_types: All the custom types in the schema
276    pub fn schema_value(
277        &self,
278        value: &data_model::SerializationModel,
279        custom_types: &HashMap<String, data_model::DataType>,
280    ) -> Result<JsonValue, Error> {
281        return match value {
282            data_model::SerializationModel::Map(value) => {
283                // Convert each field
284                let mut json_object = jzon::object::Object::new();
285                for field in self.fields.iter() {
286                    if let Some(field_value) = value.get(&field.name) {
287                        match to_json(field_value, &field.data_type, custom_types) {
288                            Ok(value) => {
289                                json_object.insert(&field.name, value);
290                            }
291                            Err(error) => return Err(error.add_field(&field.name)),
292                        }
293                    } else if let DefaultType::Required = field.default {
294                        return Err(Error {
295                            location: "".to_string(),
296                            error: ErrorCore::StructConversionMissingField(
297                                value.clone(),
298                                field.name.clone(),
299                            ),
300                        });
301                    }
302                }
303
304                // Make sure all fields are used
305                if json_object.len() != value.len() {
306                    Err(Error {
307                        location: "".to_string(),
308                        error: ErrorCore::StructConversionExcessFields(value.clone()),
309                    })
310                } else {
311                    Ok(JsonValue::Object(json_object))
312                }
313            }
314            _ => Err(Error {
315                location: "".to_string(),
316                error: ErrorCore::SerializationModel(value.clone(), "struct".to_string()),
317            }),
318        };
319    }
320}
321
322impl data_model::Array {
323    /// Creates a JSON schema from the array
324    ///
325    /// # Parameters
326    ///
327    /// custom_types: The map of all the custom types, used to check if a type is builtin or not
328    ///
329    /// dependencies: A set to add all dependencies of this array to
330    pub fn export_schema(
331        &self,
332        custom_types: &HashMap<String, data_model::DataType>,
333        dependencies: &mut HashSet<String>,
334    ) -> Result<jzon::object::Object, Error> {
335        // Add the type to dependencies
336        let ref_keyword = is_schema_type(&self.data_type, custom_types, dependencies)?;
337
338        // Construct the element schema
339        let mut element_schema = jzon::object::Object::new();
340        element_schema.insert(ref_keyword, JsonValue::String(self.data_type.clone()));
341
342        // Construct the schema
343        let mut schema = jzon::object::Object::new();
344        schema.insert(
345            "$comment",
346            JsonValue::String("An array of the same type elements".to_string()),
347        );
348        schema.insert("type", JsonValue::String("array".to_string()));
349        schema.insert("items", JsonValue::Object(element_schema));
350
351        return Ok(schema);
352    }
353
354    /// Converts a serialization model value into a JSON value of the for of this type
355    ///
356    /// # Parameters
357    ///
358    /// value: The serialization model to convert
359    ///
360    /// custom_types: All the custom types in the schema
361    pub fn schema_value(
362        &self,
363        value: &data_model::SerializationModel,
364        custom_types: &HashMap<String, data_model::DataType>,
365    ) -> Result<JsonValue, Error> {
366        return match value {
367            data_model::SerializationModel::Array(values) => {
368                match values
369                    .iter()
370                    .enumerate()
371                    .map(|(i, value)| {
372                        to_json(value, &self.data_type, custom_types).map_err(|error| (i, error))
373                    })
374                    .collect::<Result<Vec<_>, _>>()
375                {
376                    Ok(value) => Ok(JsonValue::Array(value)),
377                    Err((i, error)) => Err(error.add_element(i)),
378                }
379            }
380            _ => Err(Error {
381                location: "".to_string(),
382                error: ErrorCore::SerializationModel(value.clone(), "array".to_string()),
383            }),
384        };
385    }
386}
387
388impl data_model::Variant {
389    /// Creates a JSON schema from the variant
390    ///
391    /// # Parameters
392    ///
393    /// custom_types: The map of all the custom types, used to check if a type is builtin or not
394    ///
395    /// dependencies: A set to add all dependencies of this variant to
396    pub fn export_schema(
397        &self,
398        custom_types: &HashMap<String, data_model::DataType>,
399        dependencies: &mut HashSet<String>,
400    ) -> Result<jzon::object::Object, Error> {
401        // Construct all variant types
402        let variant_types = self
403            .data_types
404            .iter()
405            .map(|name| {
406                // Add the type to dependencies
407                let ref_keyword = is_schema_type(name, custom_types, dependencies)?;
408
409                // Create the schema
410                let mut schema = jzon::object::Object::new();
411                schema.insert(ref_keyword, JsonValue::String(name.clone()));
412
413                return Ok(JsonValue::Object(schema));
414            })
415            .collect::<Result<Vec<_>, Error>>()?;
416
417        // Create the schema
418        let mut schema = jzon::object::Object::new();
419        schema.insert(
420            "$comment",
421            JsonValue::String(
422                "A variant which will convert an input to the first type it matches".to_string(),
423            ),
424        );
425        schema.insert("anyOf", JsonValue::Array(variant_types));
426
427        return Ok(schema);
428    }
429
430    /// Converts a serialization model value into a JSON value of the for of this type
431    ///
432    /// # Parameters
433    ///
434    /// value: The serialization model to convert
435    ///
436    /// custom_types: All the custom types in the schema
437    pub fn schema_value(
438        &self,
439        value: &data_model::SerializationModel,
440        custom_types: &HashMap<String, data_model::DataType>,
441    ) -> Result<JsonValue, Error> {
442        let mut failures = Vec::new();
443        for data_type in self.data_types.iter() {
444            match to_json(value, data_type, custom_types) {
445                Ok(value) => return Ok(value),
446                Err(error) => failures.push((data_type.clone(), error)),
447            }
448        }
449
450        return Err(Error {
451            location: "".to_string(),
452            error: ErrorCore::VariantConversion(value.clone(), failures),
453        });
454    }
455}
456
457impl data_model::Enum {
458    /// Creates a JSON schema from the enum
459    ///
460    /// # Parameters
461    ///
462    /// custom_types: The map of all the custom types, used to check if a type is builtin or not
463    ///
464    /// dependencies: A set to add all dependencies of this enum to
465    pub fn export_schema(
466        &self,
467        custom_types: &HashMap<String, data_model::DataType>,
468        dependencies: &mut HashSet<String>,
469    ) -> Result<jzon::object::Object, Error> {
470        // Convert each of the enum types
471        let enum_list = self
472            .types
473            .iter()
474            .map(|value| {
475                let mut schema = if let Some(data_type) = &value.data_type {
476                    // Check if it is a custom type
477                    let ref_keyword = is_schema_type(data_type, custom_types, dependencies)?;
478
479                    // Create the internal type
480                    let mut internal_schema = jzon::object::Object::new();
481                    internal_schema.insert(ref_keyword, JsonValue::String(data_type.clone()));
482
483                    // Create the properties
484                    let mut properties = jzon::object::Object::new();
485                    properties.insert(&value.name, JsonValue::Object(internal_schema));
486
487                    // Create the schema
488                    let mut schema = jzon::object::Object::new();
489                    schema.insert("type", JsonValue::String("object".to_string()));
490                    schema.insert("additionalProperties", JsonValue::Boolean(false));
491                    schema.insert("properties", JsonValue::Object(properties));
492                    schema.insert(
493                        "required",
494                        JsonValue::Array(vec![JsonValue::String(value.name.clone())]),
495                    );
496
497                    schema
498                } else {
499                    let mut schema = jzon::object::Object::new();
500                    schema.insert("type", JsonValue::String("string".to_string()));
501                    schema.insert("pattern", JsonValue::String(value.name.clone()));
502
503                    schema
504                };
505
506                if let Some(description) = &value.description {
507                    schema.insert("description", JsonValue::String(description.clone()));
508                }
509
510                return Ok(JsonValue::Object(schema));
511            })
512            .collect::<Result<Vec<_>, Error>>()?;
513
514        // Create the
515        let mut schema = jzon::object::Object::new();
516        schema.insert(
517            "$comment",
518            JsonValue::String("A rust-like enum which can be typed".to_string()),
519        );
520        schema.insert("enum", JsonValue::Array(enum_list));
521
522        return Ok(schema);
523    }
524
525    /// Converts a serialization model value into a JSON value of the for of this type
526    ///
527    /// # Parameters
528    ///
529    /// value: The serialization model to convert
530    ///
531    /// custom_types: All the custom types in the schema
532    pub fn schema_value(
533        &self,
534        value: &data_model::SerializationModel,
535        custom_types: &HashMap<String, data_model::DataType>,
536    ) -> Result<JsonValue, Error> {
537        return match value {
538            data_model::SerializationModel::Value(value) => {
539                if self
540                    .types
541                    .iter()
542                    .filter(|enum_type| enum_type.data_type.is_none())
543                    .any(|enum_type| &enum_type.name == value)
544                {
545                    return Ok(JsonValue::String(value.clone()));
546                } else {
547                    Err(Error {
548                        location: "".to_string(),
549                        error: ErrorCore::EnumConversion(value.clone()),
550                    })
551                }
552            }
553            data_model::SerializationModel::Map(value) => {
554                if value.len() != 1 {
555                    return Err(Error {
556                        location: "".to_string(),
557                        error: ErrorCore::TypedEnumLayout(value.clone()),
558                    });
559                }
560
561                let (key, val) = value.iter().next().unwrap();
562                let enum_type = self
563                    .types
564                    .iter()
565                    .filter(|enum_type| enum_type.data_type.is_some())
566                    .find(|enum_type| &enum_type.name == key);
567                if let Some(enum_type) = enum_type {
568                    let internal_type =
569                        match to_json(val, enum_type.data_type.as_ref().unwrap(), custom_types) {
570                            Ok(value) => value,
571                            Err(error) => return Err(error.add_field(key)),
572                        };
573
574                    let mut json_object = jzon::object::Object::new();
575                    json_object.insert(key, internal_type);
576                    Ok(JsonValue::Object(json_object))
577                } else {
578                    return Err(Error {
579                        location: "".to_string(),
580                        error: ErrorCore::TypedEnumConversion(value.clone()),
581                    });
582                }
583            }
584            data_model::SerializationModel::Array(_) => Err(Error {
585                location: "".to_string(),
586                error: ErrorCore::SerializationModel(value.clone(), "enum".to_string()),
587            }),
588        };
589    }
590}
591
592impl data_model::ConstrainedType {
593    /// Creates a JSON schema from the constrained type
594    ///
595    /// # Parameters
596    ///
597    /// custom_types: The map of all the custom types, used to check if a type is builtin or not
598    ///
599    /// dependencies: A set to add all dependencies of this constrained type to
600    pub fn export_schema(
601        &self,
602        custom_types: &HashMap<String, data_model::DataType>,
603        dependencies: &mut HashSet<String>,
604    ) -> Result<jzon::object::Object, Error> {
605        // Add the type to dependencies
606        let ref_keyword = is_schema_type(&self.data_type, custom_types, dependencies)?;
607
608        // Get the list of constraints
609        let constraints = self.constraints.join(", ");
610
611        // Create the schema
612        let mut schema = jzon::object::Object::new();
613        schema.insert(
614            "$comment",
615            JsonValue::String(format!(
616                "A constrained type which must keep the following true: [{}]",
617                constraints
618            )),
619        );
620        schema.insert(ref_keyword, JsonValue::String(self.data_type.clone()));
621
622        return Ok(schema);
623    }
624
625    /// Converts a serialization model value into a JSON value of the for of this type
626    ///
627    /// # Parameters
628    ///
629    /// value: The serialization model to convert
630    ///
631    /// custom_types: All the custom types in the schema
632    pub fn schema_value(
633        &self,
634        value: &data_model::SerializationModel,
635        custom_types: &HashMap<String, data_model::DataType>,
636    ) -> Result<JsonValue, Error> {
637        return to_json(value, &self.data_type, custom_types);
638    }
639}
640
641/// Converts a serialization model of a specific type to a JSON value
642///
643/// # Parameters
644///
645/// value: The serialization model to convert
646///
647/// data_type: The data type of the serialization model
648///
649/// custom_types: All the custom types in the schema
650fn to_json(
651    value: &data_model::SerializationModel,
652    data_type: &str,
653    custom_types: &HashMap<String, data_model::DataType>,
654) -> Result<JsonValue, Error> {
655    return match data_type {
656        "boolean" => match value {
657            data_model::SerializationModel::Value(value) => match value.as_str() {
658                "true" => Ok(JsonValue::Boolean(true)),
659                "false" => Ok(JsonValue::Boolean(false)),
660                _ => Err(Error {
661                    location: "".to_string(),
662                    error: ErrorCore::BoolConversion(value.clone()),
663                }),
664            },
665            _ => Err(Error {
666                location: "".to_string(),
667                error: ErrorCore::SerializationModel(value.clone(), "boolean".to_string()),
668            }),
669        },
670        "integer" => match value {
671            data_model::SerializationModel::Value(value) => match value.parse::<i64>() {
672                Ok(value) => Ok(JsonValue::Number(jzon::number::Number::from(value))),
673                Err(_) => Err(Error {
674                    location: "".to_string(),
675                    error: ErrorCore::IntegerConversion(value.clone()),
676                }),
677            },
678            _ => Err(Error {
679                location: "".to_string(),
680                error: ErrorCore::SerializationModel(value.clone(), "integer".to_string()),
681            }),
682        },
683        "number" => match value {
684            data_model::SerializationModel::Value(value) => match value.parse::<f64>() {
685                Ok(value) => Ok(JsonValue::Number(jzon::number::Number::from(value))),
686                Err(_) => Err(Error {
687                    location: "".to_string(),
688                    error: ErrorCore::FloatConversion(value.clone()),
689                }),
690            },
691            _ => Err(Error {
692                location: "".to_string(),
693                error: ErrorCore::SerializationModel(value.clone(), "number".to_string()),
694            }),
695        },
696        "string" => match value {
697            data_model::SerializationModel::Value(value) => Ok(JsonValue::String(value.clone())),
698            _ => Err(Error {
699                location: "".to_string(),
700                error: ErrorCore::SerializationModel(value.clone(), "string".to_string()),
701            }),
702        },
703        _ => {
704            if let Some(custom_type) = custom_types.get(data_type) {
705                custom_type.schema_value(value, custom_types)
706            } else {
707                Err(Error {
708                    location: "".to_string(),
709                    error: ErrorCore::UnknownType(data_type.to_string()),
710                })
711            }
712        }
713    };
714}
715
716/// Checks if a given type is a builtin schema type or a custom type, if it is a custom type then it adds it to the dependencies
717///
718/// # Parameters
719///
720/// name: The name of the type
721///
722/// custom_types: All the custom types in the schema
723///
724/// dependencies: The set to add the type to if it is a custom type
725///
726/// # Errors
727///
728/// If the type is not a custom type or a builtin type then an error it thrown
729fn is_schema_type(
730    name: &str,
731    custom_types: &HashMap<String, data_model::DataType>,
732    dependencies: &mut HashSet<String>,
733) -> Result<&'static str, Error> {
734    return Ok(if let Some(_) = custom_types.get(name) {
735        dependencies.insert(name.to_string());
736        "$ref"
737    } else {
738        if !vec!["boolean", "integer", "number", "string"].contains(&name) {
739            return Err(Error {
740                location: "".to_string(),
741                error: ErrorCore::UnknownType(name.to_string()),
742            });
743        }
744        "type"
745    });
746}
747
748/// Errors for when converting generic data models into JSON schema data models
749/// including location
750#[derive(Debug, Clone)]
751pub struct Error {
752    /// The location where the error occured
753    pub location: String,
754    /// The actual error that occured
755    pub error: ErrorCore,
756}
757
758impl Error {
759    /// Sets the current location to be the field of the given base
760    ///
761    /// # Parameters
762    ///
763    /// base: The base to set in the location
764    fn add_field(self, base: &str) -> Error {
765        let location = format!(".{}{}", base, self.location);
766
767        return Error {
768            location,
769            error: self.error,
770        };
771    }
772
773    /// Sets the current location to be the element of a field of the given base
774    ///
775    /// # Parameters
776    ///
777    /// index: The index of the field
778    fn add_element(self, index: usize) -> Error {
779        let location = format!("[{}]{}", index, self.location);
780
781        return Error {
782            location,
783            error: self.error,
784        };
785    }
786}
787
788impl fmt::Display for Error {
789    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
790        return write!(f, "{}: {}", self.location, self.error);
791    }
792}
793
794impl From<data_model::Error> for Error {
795    fn from(value: data_model::Error) -> Self {
796        return Error {
797            location: value.location.clone(),
798            error: ErrorCore::MacroError(value),
799        };
800    }
801}
802
803/// Errors for when converting generic data models into JSON schema data models
804#[derive(thiserror::Error, Debug, Clone)]
805pub enum ErrorCore {
806    /// Unable to find the type id
807    #[error("The type id {:?} does not exist in the model", .0)]
808    UnknownID(String),
809    /// The type is neither a builtin type or a custom type
810    #[error("The type id {:?} does not exist in the model or as a builtin type", .0)]
811    UnknownType(String),
812    /// The type must be a struct
813    #[error("The type id {:?} must refer to a struct when inheriting", .0)]
814    EnheritStruct(String),
815    /// Serialization model has an incompatible type
816    #[error("The serialization model {:?} is incompatible with the type: {:}", .0, .1)]
817    SerializationModel(data_model::SerializationModel, String),
818    /// Unable to convert to boolean
819    #[error("Unable to convert {:?} to a boolean", .0)]
820    BoolConversion(String),
821    /// Unable to convert to integer
822    #[error("Unable to convert {:?} to an integer", .0)]
823    IntegerConversion(String),
824    /// Unable to convert to float
825    #[error("Unable to convert {:?} to a float", .0)]
826    FloatConversion(String),
827    /// Unable to convert to enum
828    #[error("Unable to convert {:?} to an enum", .0)]
829    EnumConversion(String),
830    /// Unable to convert to typed enum
831    #[error("Unable to convert {:?} to a typed enum", .0)]
832    TypedEnumConversion(HashMap<String, data_model::SerializationModel>),
833    /// The map had more or less than one element when converting to a typed enum
834    #[error("Unable to convert {:?} to a typed enum because it did not have a single field", .0)]
835    TypedEnumLayout(HashMap<String, data_model::SerializationModel>),
836    /// Unable to convert to variant
837    #[error("Unable to convert {:?} to a variant with the following errors: {:?}", .0, .1)]
838    VariantConversion(data_model::SerializationModel, Vec<(String, Error)>),
839    /// Unable to convert to struct due to missing field
840    #[error("Unable to convert {:?} to an struct because it is missing field {:}", .0, .1)]
841    StructConversionMissingField(HashMap<String, data_model::SerializationModel>, String),
842    /// Unable to convert to struct due to excess fields
843    #[error("Unable to convert {:?} to an struct because it has excess fields", .0)]
844    StructConversionExcessFields(HashMap<String, data_model::SerializationModel>),
845    /// Error expanding macros
846    #[error("An error occured when expanding macros: {:?}", .0)]
847    MacroError(data_model::Error),
848}