ocsf_codegen/
dictionary.rs

1use std::fmt::Debug;
2
3use crate::module::Module;
4
5use super::*;
6use codegen::{Field, Variant};
7// use regex::Regex;
8use 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    // #[serde(alias="enum")]
17    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 mut output_scope = Scope::new();
43
44    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                // .field("name", name)
207                // .finish(),
208            }
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}