Skip to main content

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
610            .constraints
611            .iter()
612            .map(|constraint| constraint.export_schema())
613            .collect::<Vec<_>>()
614            .join(", ");
615
616        // Create the schema
617        let mut schema = jzon::object::Object::new();
618        schema.insert(
619            "$comment",
620            JsonValue::String(format!(
621                "A constrained type which must keep the following true: [{}]",
622                constraints
623            )),
624        );
625        schema.insert(ref_keyword, JsonValue::String(self.data_type.clone()));
626
627        return Ok(schema);
628    }
629
630    /// Converts a serialization model value into a JSON value of the for of this type
631    ///
632    /// # Parameters
633    ///
634    /// value: The serialization model to convert
635    ///
636    /// custom_types: All the custom types in the schema
637    pub fn schema_value(
638        &self,
639        value: &data_model::SerializationModel,
640        custom_types: &HashMap<String, data_model::DataType>,
641    ) -> Result<JsonValue, Error> {
642        return to_json(value, &self.data_type, custom_types);
643    }
644}
645
646impl data_model::Constraint {
647    /// Creates a JSON schema from the constrained type
648    pub fn export_schema(&self) -> String {
649        return match self {
650            Self::Arithmetic(value) => value.clone(),
651            Self::Function(value) => format!("{value}(x)"),
652        };
653    }
654}
655
656/// Converts a serialization model of a specific type to a JSON value
657///
658/// # Parameters
659///
660/// value: The serialization model to convert
661///
662/// data_type: The data type of the serialization model
663///
664/// custom_types: All the custom types in the schema
665fn to_json(
666    value: &data_model::SerializationModel,
667    data_type: &str,
668    custom_types: &HashMap<String, data_model::DataType>,
669) -> Result<JsonValue, Error> {
670    return match data_type {
671        "boolean" => match value {
672            data_model::SerializationModel::Value(value) => match value.as_str() {
673                "true" => Ok(JsonValue::Boolean(true)),
674                "false" => Ok(JsonValue::Boolean(false)),
675                _ => Err(Error {
676                    location: "".to_string(),
677                    error: ErrorCore::BoolConversion(value.clone()),
678                }),
679            },
680            _ => Err(Error {
681                location: "".to_string(),
682                error: ErrorCore::SerializationModel(value.clone(), "boolean".to_string()),
683            }),
684        },
685        "integer" => match value {
686            data_model::SerializationModel::Value(value) => match value.parse::<i64>() {
687                Ok(value) => Ok(JsonValue::Number(jzon::number::Number::from(value))),
688                Err(_) => Err(Error {
689                    location: "".to_string(),
690                    error: ErrorCore::IntegerConversion(value.clone()),
691                }),
692            },
693            _ => Err(Error {
694                location: "".to_string(),
695                error: ErrorCore::SerializationModel(value.clone(), "integer".to_string()),
696            }),
697        },
698        "number" => match value {
699            data_model::SerializationModel::Value(value) => match value.parse::<f64>() {
700                Ok(value) => Ok(JsonValue::Number(jzon::number::Number::from(value))),
701                Err(_) => Err(Error {
702                    location: "".to_string(),
703                    error: ErrorCore::FloatConversion(value.clone()),
704                }),
705            },
706            _ => Err(Error {
707                location: "".to_string(),
708                error: ErrorCore::SerializationModel(value.clone(), "number".to_string()),
709            }),
710        },
711        "string" => match value {
712            data_model::SerializationModel::Value(value) => Ok(JsonValue::String(value.clone())),
713            _ => Err(Error {
714                location: "".to_string(),
715                error: ErrorCore::SerializationModel(value.clone(), "string".to_string()),
716            }),
717        },
718        _ => {
719            if let Some(custom_type) = custom_types.get(data_type) {
720                custom_type.schema_value(value, custom_types)
721            } else {
722                Err(Error {
723                    location: "".to_string(),
724                    error: ErrorCore::UnknownType(data_type.to_string()),
725                })
726            }
727        }
728    };
729}
730
731/// 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
732///
733/// # Parameters
734///
735/// name: The name of the type
736///
737/// custom_types: All the custom types in the schema
738///
739/// dependencies: The set to add the type to if it is a custom type
740///
741/// # Errors
742///
743/// If the type is not a custom type or a builtin type then an error it thrown
744fn is_schema_type(
745    name: &str,
746    custom_types: &HashMap<String, data_model::DataType>,
747    dependencies: &mut HashSet<String>,
748) -> Result<&'static str, Error> {
749    return Ok(if let Some(_) = custom_types.get(name) {
750        dependencies.insert(name.to_string());
751        "$ref"
752    } else {
753        if !vec!["boolean", "integer", "number", "string"].contains(&name) {
754            return Err(Error {
755                location: "".to_string(),
756                error: ErrorCore::UnknownType(name.to_string()),
757            });
758        }
759        "type"
760    });
761}
762
763/// Errors for when converting generic data models into JSON schema data models
764/// including location
765#[derive(Debug, Clone)]
766pub struct Error {
767    /// The location where the error occured
768    pub location: String,
769    /// The actual error that occured
770    pub error: ErrorCore,
771}
772
773impl Error {
774    /// Sets the current location to be the field of the given base
775    ///
776    /// # Parameters
777    ///
778    /// base: The base to set in the location
779    fn add_field(self, base: &str) -> Error {
780        let location = format!(".{}{}", base, self.location);
781
782        return Error {
783            location,
784            error: self.error,
785        };
786    }
787
788    /// Sets the current location to be the element of a field of the given base
789    ///
790    /// # Parameters
791    ///
792    /// index: The index of the field
793    fn add_element(self, index: usize) -> Error {
794        let location = format!("[{}]{}", index, self.location);
795
796        return Error {
797            location,
798            error: self.error,
799        };
800    }
801}
802
803impl fmt::Display for Error {
804    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
805        return write!(f, "{}: {}", self.location, self.error);
806    }
807}
808
809impl From<data_model::Error> for Error {
810    fn from(value: data_model::Error) -> Self {
811        return Error {
812            location: value.location.clone(),
813            error: ErrorCore::MacroError(value),
814        };
815    }
816}
817
818/// Errors for when converting generic data models into JSON schema data models
819#[derive(thiserror::Error, Debug, Clone)]
820pub enum ErrorCore {
821    /// Unable to find the type id
822    #[error("The type id {:?} does not exist in the model", .0)]
823    UnknownID(String),
824    /// The type is neither a builtin type or a custom type
825    #[error("The type id {:?} does not exist in the model or as a builtin type", .0)]
826    UnknownType(String),
827    /// The type must be a struct
828    #[error("The type id {:?} must refer to a struct when inheriting", .0)]
829    EnheritStruct(String),
830    /// Serialization model has an incompatible type
831    #[error("The serialization model {:?} is incompatible with the type: {:}", .0, .1)]
832    SerializationModel(data_model::SerializationModel, String),
833    /// Unable to convert to boolean
834    #[error("Unable to convert {:?} to a boolean", .0)]
835    BoolConversion(String),
836    /// Unable to convert to integer
837    #[error("Unable to convert {:?} to an integer", .0)]
838    IntegerConversion(String),
839    /// Unable to convert to float
840    #[error("Unable to convert {:?} to a float", .0)]
841    FloatConversion(String),
842    /// Unable to convert to enum
843    #[error("Unable to convert {:?} to an enum", .0)]
844    EnumConversion(String),
845    /// Unable to convert to typed enum
846    #[error("Unable to convert {:?} to a typed enum", .0)]
847    TypedEnumConversion(HashMap<String, data_model::SerializationModel>),
848    /// The map had more or less than one element when converting to a typed enum
849    #[error("Unable to convert {:?} to a typed enum because it did not have a single field", .0)]
850    TypedEnumLayout(HashMap<String, data_model::SerializationModel>),
851    /// Unable to convert to variant
852    #[error("Unable to convert {:?} to a variant with the following errors: {:?}", .0, .1)]
853    VariantConversion(data_model::SerializationModel, Vec<(String, Error)>),
854    /// Unable to convert to struct due to missing field
855    #[error("Unable to convert {:?} to an struct because it is missing field {:}", .0, .1)]
856    StructConversionMissingField(HashMap<String, data_model::SerializationModel>, String),
857    /// Unable to convert to struct due to excess fields
858    #[error("Unable to convert {:?} to an struct because it has excess fields", .0)]
859    StructConversionExcessFields(HashMap<String, data_model::SerializationModel>),
860    /// Error expanding macros
861    #[error("An error occured when expanding macros: {:?}", .0)]
862    MacroError(data_model::Error),
863}