Skip to main content

termite_dmg/dart/
mod.rs

1//!
2//! This module handles generation of Dart code to support a data model, it
3//! includes the ability to create a source file, (de)serialization and
4//! documentation.
5//!
6//! For any data model to work the termite dependency must be generated from
7//! get_termite_dependency() and be saved as "termite.dart" and
8//! "termite-types.dart" at a location where they can be included as "import
9//! 'termite.dart';" and "import 'termite-types.dart';"
10//!
11
12use std::collections::{HashMap, HashSet};
13
14use indoc::formatdoc;
15
16use crate::data_model;
17
18mod type_array;
19mod type_constrained;
20mod type_enum;
21mod type_struct;
22mod type_variant;
23
24/// Obtains the base termite Dart dependencies required for all generated data
25/// models, must be saved as "termite.dart" and "termite-types.dart"
26pub fn get_termite_dependency() -> (&'static str, &'static str) {
27    return (
28        include_str!("termite.dart"),
29        include_str!("termite-types.dart"),
30    );
31}
32
33/// Obtains the JSON interface source for reading and writing json objects
34pub fn get_json_interface() -> &'static str {
35    return include_str!("termite-json.dart");
36}
37
38impl data_model::DataModel {
39    /// Generates the Dart source code for the entire data model
40    ///
41    /// # Parameters
42    ///
43    /// indent: The number of spaces per indentation level
44    pub fn get_dart<'a>(&self, indent: usize) -> Result<String, data_model::Error> {
45        let header = if let Some(header) = self.headers.get("dart") {
46            let value = data_model::expand_macros(
47                &data_model::SerializationModel::Value(header.clone()),
48                &self.macros,
49                &mut HashSet::new(),
50            )?;
51            if let data_model::SerializationModel::Value(value) = value {
52                value
53            } else {
54                return Err(data_model::Error {
55                    location: "".to_string(),
56                    error: data_model::ErrorCore::HeaderMacro(header.clone()),
57                });
58            }
59        } else {
60            "".to_string()
61        };
62
63        let footer = if let Some(footer) = self.footers.get("dart") {
64            let value = data_model::expand_macros(
65                &data_model::SerializationModel::Value(footer.clone()),
66                &self.macros,
67                &mut HashSet::new(),
68            )?;
69            if let data_model::SerializationModel::Value(value) = value {
70                value
71            } else {
72                return Err(data_model::Error {
73                    location: "".to_string(),
74                    error: data_model::ErrorCore::FooterMacro(footer.clone()),
75                });
76            }
77        } else {
78            "".to_string()
79        };
80
81        let data_types = self
82            .data_types
83            .iter()
84            .map(|data_type| data_type.get_dart(indent, &self.macros))
85            .collect::<Result<Vec<String>, data_model::Error>>()?
86            .join("\n\n");
87
88        return Ok(formatdoc!(
89            "
90            // Generated with the Termite Data Model Generator
91
92            // ignore_for_file: no_leading_underscores_for_local_identifiers, non_constant_identifier_names, unnecessary_string_interpolations, camel_case_types, empty_constructor_bodies, camel_case_extensions
93
94            import 'termite.dart' as termite;
95            import 'termite-types.dart';
96
97            {header}
98
99            {data_types}
100
101            {footer}
102            "
103        ));
104    }
105}
106
107impl data_model::DataType {
108    /// Generates the Dart source code for a the type
109    ///
110    /// # Parameters
111    ///
112    /// indent: The number of spaces per indentation level
113    ///
114    /// macros: The macros defined in the data model used for expanding default values
115    pub fn get_dart<'a>(
116        &self,
117        indent: usize,
118        macros: &'a HashMap<String, data_model::SerializationModel>,
119    ) -> Result<String, data_model::Error> {
120        let description = match &self.description {
121            Some(description) => format!("/// {description}\n"),
122            None => "".to_string(),
123        };
124
125        return Ok(format!(
126            "{description}{data}",
127            data = self.data.get_dart(&self.name, indent, macros)?
128        ));
129    }
130}
131
132impl data_model::DataTypeData {
133    /// Generates the Dart source code for a the type data
134    ///
135    /// # Parameters
136    ///
137    /// name: The name of the type
138    ///
139    /// indent: The number of spaces per indentation level
140    ///
141    /// macros: The macros defined in the data model used for expanding default values
142    fn get_dart<'a>(
143        &self,
144        name: &str,
145        indent: usize,
146        macros: &'a HashMap<String, data_model::SerializationModel>,
147    ) -> Result<String, data_model::Error> {
148        return match &self {
149            data_model::DataTypeData::Enum(data) => Ok(data.get_dart(name, indent)),
150            data_model::DataTypeData::Struct(data) => data.get_dart(name, indent, macros),
151            data_model::DataTypeData::Variant(data) => Ok(data.get_dart(name, indent)),
152            data_model::DataTypeData::Array(data) => Ok(data.get_dart(name, indent)),
153            data_model::DataTypeData::ConstrainedType(data) => Ok(data.get_dart(name, indent)),
154        };
155    }
156}