1use std::fmt::Debug;
2
3use crate::module::Module;
4
5use super::*;
6use codegen::{Field, Variant};
7use serde::{Deserialize, Deserializer, Serialize};
9
10#[allow(dead_code)]
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct DictAttribute {
13 caption: String,
14 default: Option<i32>,
15 description: String,
16 attr_enum: Option<String>,
18 is_array: Option<bool>,
19 sibling: Option<String>,
20 #[serde(alias = "type")]
21 attr_type: TypeNames,
22}
23
24#[allow(dead_code)]
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct DictType {
27 caption: String,
28 description: Option<String>,
29 max_len: Option<String>,
30 observable: Option<String>,
31 range: Option<String>,
32 regex: Option<String>,
33 value_type: Option<String>,
34 type_name: Option<String>,
35 values: Option<String>,
36}
37
38pub fn generate_dictionary_entries(
39 paths: &DirPaths,
40 root_module: &mut Module,
41) -> Result<(), Box<dyn Error>> {
42 let dictionary_module = root_module
45 .children
46 .get_mut("dictionary")
47 .expect("Couldn't get dictionary module from root?");
48
49 let dict_file = read_file_to_value(&format!("{}/dictionary.json", paths.schema_path))?;
50
51 dictionary_module.scope.writeln(format!(
52 "//! {}\n\n",
53 dict_file.get("description").unwrap().as_str().unwrap_or("")
54 ));
55
56 dictionary_module.scope.add_generation_timestamp_comment();
57 dictionary_module.scope.writeln("use serde::{Serialize};");
58
59 dictionary_module
60 .scope
61 .new_struct("DictAttribute")
62 .vis("pub")
63 .doc("A generic way of identifying attributes from the dictionary.")
64 .derive("Debug,Clone,Serialize")
65 .push_field(Field::new("caption", "&'static str").vis("pub").to_owned())
66 .push_field(Field::new("default", "Option<i32>").vis("pub").to_owned())
67 .push_field(
68 Field::new("description", "&'static str")
69 .vis("pub")
70 .to_owned(),
71 )
72 .push_field(
73 Field::new("attr_enum", "Option<&'static str>")
74 .vis("pub")
75 .to_owned(),
76 )
77 .push_field(Field::new("is_array", "Option<bool>").vis("pub").to_owned())
78 .push_field(
79 Field::new("sibling", "Option<&'static str>")
80 .vis("pub")
81 .to_owned(),
82 )
83 .push_field(
84 Field::new("attr_type", "TypeNames")
85 .vis("pub")
86 .doc("`attr_type` maps to 'type' in the actual schema.")
87 .annotation("#[serde(alias=\"type\")]")
88 .to_owned(),
89 );
90
91 dictionary_module
92 .scope
93 .new_enum("TypeNames")
94 .vis("pub")
95 .doc("Attribute variable types.")
96 .derive("Debug,Clone,Serialize")
97 .push_variant(Variant::new("Integer"))
98 .push_variant(Variant::new("Json"))
99 .push_variant(Variant::new("String"))
100 .push_variant(Variant::new("Timestamp"))
101 .push_variant(Variant::new("Boolean"))
102 .push_variant(Variant::new("NotSupported{ name: &'static str }"));
103
104 dictionary_module
105 .scope
106 .new_struct("DictType")
107 .vis("pub")
108 .doc("Trying to annotate the attribute types.")
109 .derive("Debug,Clone,Serialize")
110 .push_field(Field::new("caption", "&'static str").vis("pub").to_owned())
111 .push_field(
112 Field::new("description", "Option<&'static str>")
113 .vis("pub")
114 .to_owned(),
115 )
116 .push_field(
117 Field::new("max_len", "Option<&'static str>")
118 .vis("pub")
119 .to_owned(),
120 )
121 .push_field(
122 Field::new("observable", "Option<&'static str>")
123 .vis("pub")
124 .to_owned(),
125 )
126 .push_field(
127 Field::new("range", "Option<&'static str>")
128 .vis("pub")
129 .to_owned(),
130 )
131 .push_field(
132 Field::new("regex", "Option<&'static str>")
133 .vis("pub")
134 .to_owned(),
135 )
136 .push_field(
137 Field::new("value_type", "Option<&'static str>")
138 .vis("pub")
139 .to_owned(),
140 )
141 .push_field(
142 Field::new("type_name", "Option<&'static str>")
143 .vis("pub")
144 .to_owned(),
145 )
146 .push_field(
147 Field::new("values", "Option<&'static str>")
148 .vis("pub")
149 .to_owned(),
150 );
151
152 dict_file
153 .get("attributes")
154 .unwrap()
155 .as_object()
156 .unwrap()
157 .into_iter()
158 .for_each(|(attribute_name, attribute_value)| {
159 trace!("attribute_value: {attribute_value:#?}");
160 let attribute: DictAttribute =
161 serde_json::from_value(attribute_value.to_owned()).unwrap();
162 trace!("{attribute:#?}");
163 #[allow(clippy::single_char_add_str)]
164 dictionary_module.scope.writeln("");
165 let thing_to_push = format!(
166 "pub const {}: DictAttribute = {:#?};\n",
167 attribute_name.to_uppercase(),
168 attribute
169 );
170 dictionary_module.scope.writeln(&format!(
171 "/// {} - {}",
172 attribute.caption,
173 fix_docstring(attribute.description, Some("///"))
174 ));
175 dictionary_module.scope.writeln(&thing_to_push);
176 });
177
178 write_source_file(
179 &format!("{}src/dictionary.rs", paths.destination_path),
180 &dictionary_module.scope.to_string(),
181 )?;
182
183 Ok(())
184}
185
186#[derive(Clone, Serialize)]
187enum TypeNames {
188 Integer,
189 Json,
190 String,
191 Timestamp,
192 Boolean,
193 NotSupported { name: String },
194}
195
196impl Debug for TypeNames {
197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198 match self {
199 Self::Boolean => write!(f, "TypeNames::Boolean"),
200 Self::Integer => write!(f, "TypeNames::Integer"),
201 Self::Json => write!(f, "TypeNames::Json"),
202 Self::String => write!(f, "TypeNames::String"),
203 Self::Timestamp => write!(f, "TypeNames::Timestamp"),
204 Self::NotSupported { name } => {
205 write!(f, "TypeNames::NotSupported{{ name: \"{name}\" }}")
206 }
209 }
210 }
211}
212
213impl From<&str> for TypeNames {
214 fn from(value: &str) -> Self {
215 match value {
216 "boolean_t" => Self::Boolean,
217 "string_t" => Self::String,
218 "integer_t" => Self::Integer,
219 "json_t" => Self::Json,
220 "timestamp_t" => Self::Timestamp,
221 _ => Self::NotSupported {
222 name: value.to_string(),
223 },
224 }
225 }
226}
227
228impl<'de> Deserialize<'de> for TypeNames {
229 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
230 where
231 D: Deserializer<'de>,
232 {
233 let stringval = String::deserialize(deserializer)?.to_lowercase();
234 let result: TypeNames = stringval.into();
235 Ok(result)
236 }
237}
238
239impl From<String> for TypeNames {
240 fn from(value: String) -> Self {
241 value.as_str().into()
242 }
243}
244
245impl From<serde_json::Value> for TypeNames {
246 fn from(value: serde_json::Value) -> Self {
247 match value {
248 Value::String(val) => val.as_str().into(),
249 _ => panic!("Unsupported value type: {value:?}"),
250 }
251 }
252}