ocsf_codegen/
categories.rs

1use crate::module::Module;
2use crate::*;
3use codegen::{Function, Variant};
4use serde::Deserialize;
5
6// TODO: categories from the categories file
7
8#[derive(Debug, Deserialize)]
9pub struct Category {
10    pub caption: String,
11    pub description: String,
12    pub uid: u32,
13}
14
15pub fn generate_categories(
16    paths: &DirPaths,
17    root_module: &mut Module,
18) -> Result<(), Box<dyn Error>> {
19    let categories_module = root_module
20        .children
21        .get_mut("categories")
22        .expect("Couldn't find categories module in root module?");
23
24    let categories_file = read_file_to_value(&format!("{}categories.json", paths.schema_path))?;
25
26    let categories_description: String = categories_file
27        .get("description")
28        .unwrap_or(&Value::String("".to_string()))
29        .as_str()
30        .unwrap()
31        .to_string();
32
33    categories_module.scope.raw(r#"//! OCSF Category data"#);
34    categories_module.scope.raw("//!".to_string());
35    categories_module
36        .scope
37        .raw(format!("//! {categories_description}"));
38
39    categories_module.scope.add_generation_timestamp_comment();
40
41    let enum_name = "Category";
42
43    // enum Category
44    let mut category_enum = codegen::Enum::new(enum_name);
45    category_enum.vis("pub").doc(&categories_description);
46
47    // impl From<Category> for u8
48    let mut category_to_u8 = Function::new("from");
49    category_to_u8.arg("input", enum_name).ret("u8");
50    category_to_u8.line("match input {");
51
52    // impl TryFrom<u8> for Category
53    let mut u8_to_category = Function::new("try_from");
54    u8_to_category
55        .arg("input", "u8")
56        .ret("Result<Self, String>");
57    // u8_to_category.line("type Error = String;");
58    u8_to_category.line("let res = match input {");
59
60    // generate details based on the attributes
61    if let Some(attributes) = categories_file.get("attributes") {
62        if let Some(attributes_object) = attributes.as_object() {
63            attributes_object.into_iter().for_each(|(key, value)| {
64                let category: Category = serde_json::from_value(value.clone()).unwrap();
65                let variant_name = collapsed_title_case(key);
66
67                let variant_docstring = vec![
68                    format!("/// {}", category.description),
69                    "///".to_string(),
70                    format!("/// `uid={}`", category.uid),
71                ];
72
73                let variant = Variant::new(&variant_name)
74                    .annotation(variant_docstring.join("\n"))
75                    .to_owned();
76                category_enum.push_variant(variant);
77                category_to_u8.line(format!(
78                    "{}::{} => {},",
79                    enum_name, variant_name, category.uid
80                ));
81                u8_to_category.line(format!(
82                    "{} => {}::{},",
83                    category.uid, enum_name, variant_name
84                ));
85            })
86        }
87    }
88
89    // finish up From<Category> for u8
90    category_to_u8.line("}");
91    let mut category_to_u8_impl = codegen::Impl::new("u8");
92    category_to_u8_impl.impl_trait(format!("From<{enum_name}>"));
93    category_to_u8_impl.push_fn(category_to_u8);
94
95    // finish up TryFrom<u8> for Category
96    u8_to_category.line("_ => return Err(format!(\"Invalid value specified: {input}\")),\n");
97    u8_to_category.line("};");
98    u8_to_category.line("Ok(res)");
99    let mut u8_to_category_impl = codegen::Impl::new(enum_name);
100    u8_to_category_impl
101        .impl_trait("TryFrom<u8>")
102        .associate_type("Error", "String");
103    u8_to_category_impl.push_fn(u8_to_category);
104
105    categories_module.scope.push_enum(category_enum.to_owned());
106    categories_module
107        .scope
108        .push_impl(category_to_u8_impl.to_owned());
109    categories_module
110        .scope
111        .push_impl(u8_to_category_impl.to_owned());
112
113    write_source_file(
114        &format!("{}src/categories.rs", paths.destination_path),
115        &categories_module.scope.to_string(),
116    )
117}