assemblyline_models/
serialize.rs

1
2use serde::de;
3use serde::Deserialize;
4
5pub fn deserialize_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
6where
7    D: de::Deserializer<'de>,
8{
9    #[derive(Deserialize)]
10    #[serde(untagged)]
11    enum Value<'a> {
12        Raw(bool),
13        Str(&'a str)
14    }
15
16    let value: Value = de::Deserialize::deserialize(deserializer)?;
17
18    match value {
19        Value::Raw(value) => Ok(value),
20        Value::Str(string) => Ok(string.trim().eq_ignore_ascii_case("true")),
21    }    
22}
23
24pub fn deserialize_string_or_list<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
25where
26    D: de::Deserializer<'de>,
27{
28    #[derive(Deserialize)]
29    #[serde(untagged)]
30    enum Value {
31        Str(String),
32        Vec(Vec<String>)
33    }
34
35    let value: Value = de::Deserialize::deserialize(deserializer)?;
36
37    match value {
38        Value::Vec(value) => Ok(value),
39        Value::Str(string) => Ok(vec![string]),
40    }    
41}
42
43
44#[cfg(test)]
45pub mod test {
46
47    use assemblyline_markings::classification::ClassificationParser;
48    use chrono::{DateTime, Utc};
49    use serde::{Deserialize, Serialize};
50    use serde_json::json;
51    use struct_metadata::Described;
52    use pretty_assertions::assert_eq;
53
54    use crate::{ElasticMeta, types::classification::{ClassificationString, ExpandingClassification}};
55
56    pub fn setup_classification() -> std::sync::Arc<ClassificationParser> {
57        let parser = std::sync::Arc::new(ClassificationParser::new(serde_json::from_str(r#"{"enforce":true,"dynamic_groups":false,"dynamic_groups_type":"all","levels":[{"aliases":["OPEN"],"css":{"color":"default"},"description":"N/A","lvl":1,"name":"LEVEL 0","short_name":"L0"},{"aliases":[],"css":{"color":"default"},"description":"N/A","lvl":5,"name":"LEVEL 1","short_name":"L1"},{"aliases":[],"css":{"color":"default"},"description":"N/A","lvl":15,"name":"LEVEL 2","short_name":"L2"}],"required":[{"aliases":["LEGAL"],"description":"N/A","name":"LEGAL DEPARTMENT","short_name":"LE","require_lvl":null,"is_required_group":false},{"aliases":["ACC"],"description":"N/A","name":"ACCOUNTING","short_name":"AC","require_lvl":null,"is_required_group":false},{"aliases":[],"description":"N/A","name":"ORIGINATOR CONTROLLED","short_name":"ORCON","require_lvl":null,"is_required_group":true},{"aliases":[],"description":"N/A","name":"NO CONTRACTOR ACCESS","short_name":"NOCON","require_lvl":null,"is_required_group":true}],"groups":[{"aliases":[],"auto_select":false,"description":"N/A","name":"GROUP A","short_name":"A","solitary_display_name":null},{"aliases":[],"auto_select":false,"description":"N/A","name":"GROUP B","short_name":"B","solitary_display_name":null},{"aliases":[],"auto_select":false,"description":"N/A","name":"GROUP X","short_name":"X","solitary_display_name":"XX"}],"subgroups":[{"aliases":["R0"],"auto_select":false,"description":"N/A","name":"RESERVE ONE","short_name":"R1","require_group":null,"limited_to_group":null},{"aliases":[],"auto_select":false,"description":"N/A","name":"RESERVE TWO","short_name":"R2","require_group":"X","limited_to_group":null},{"aliases":[],"auto_select":false,"description":"N/A","name":"RESERVE THREE","short_name":"R3","require_group":null,"limited_to_group":"X"}],"restricted":"L2","unrestricted":"L0"}"#).unwrap()).unwrap());
58        crate::types::classification::set_global_classification(parser.clone());
59        parser
60    }
61
62    #[derive(Described, Serialize, Deserialize)]
63    #[metadata_type(ElasticMeta)]
64    struct SubObject {
65        classification: ClassificationString,
66    }
67
68    #[derive(Described, Serialize, Deserialize)]
69    #[metadata_type(ElasticMeta)]
70    struct TestObject {
71        #[serde(flatten)]
72        classification: ExpandingClassification,
73        other_data: ClassificationString,
74        expiry_ts: DateTime<Utc>,
75        data: SubObject,
76    }
77
78    // Test that the classification components get expanded as expected
79    #[test]
80    fn classification_serialize() {
81        let parser = setup_classification();
82        let time = DateTime::parse_from_rfc3339("2001-05-01T01:59:59.001Z").unwrap();
83        let sample = TestObject {
84            classification: ExpandingClassification::new("L0//LE//REL    A".to_owned(), &parser).unwrap(),
85            other_data: ClassificationString::new("L2//AC//REL          B".to_owned(), &parser).unwrap(),
86            expiry_ts: time.into(),
87            data: SubObject { 
88                classification: ClassificationString::new("L1//LE   ".to_owned(), &parser).unwrap(),
89            },
90        };
91        assert_eq!(serde_json::to_value(sample).unwrap(), json!({
92            "classification": "L0//LE//REL A",
93            "__access_lvl__": 1,
94            "__access_req__": ["LE"],
95            "__access_grp1__": ["A"],
96            "__access_grp2__": ["__EMPTY__"],
97            "other_data": "L2//AC//REL B",
98            "expiry_ts": "2001-05-01T01:59:59.001Z",
99            "data": {
100                "classification": "L1//LE"
101            }
102        }));
103    }
104}