oca_bundle_semantics/state/oca/overlay/
label.rs

1use crate::state::{attribute::Attribute, oca::Overlay};
2use isolang::Language;
3use oca_ast_semantics::ast::OverlayType;
4use said::derivation::HashFunctionCode;
5use said::{sad::SerializationFormats, sad::SAD};
6use serde::{ser::SerializeMap, ser::SerializeSeq, Deserialize, Serialize, Serializer};
7use std::any::Any;
8use std::collections::HashMap;
9
10pub trait Labels {
11    fn set_label(&mut self, l: Language, label: String);
12    fn add_category_label(&mut self, l: Language, label: String);
13}
14
15impl Labels for Attribute {
16    fn set_label(&mut self, l: Language, label: String) {
17        match self.labels {
18            Some(ref mut labels) => {
19                labels.insert(l, label);
20            }
21            None => {
22                let mut labels = HashMap::new();
23                labels.insert(l, label);
24                self.labels = Some(labels);
25            }
26        }
27    }
28    fn add_category_label(&mut self, l: Language, label: String) {
29        match self.category_labels {
30            Some(ref mut category_labels) => {
31                category_labels.insert(l, label);
32            }
33            None => {
34                let mut category_labels = HashMap::new();
35                category_labels.insert(l, label);
36                self.category_labels = Some(category_labels);
37            }
38        }
39    }
40}
41
42pub fn serialize_labels<S>(attributes: &HashMap<String, String>, s: S) -> Result<S::Ok, S::Error>
43where
44    S: Serializer,
45{
46    use std::collections::BTreeMap;
47
48    let mut ser = s.serialize_map(Some(attributes.len()))?;
49    let sorted_attributes: BTreeMap<_, _> = attributes.iter().collect();
50    for (k, v) in sorted_attributes {
51        ser.serialize_entry(k, v)?;
52    }
53    ser.end()
54}
55
56pub fn serialize_categories<S>(attributes: &[String], s: S) -> Result<S::Ok, S::Error>
57where
58    S: Serializer,
59{
60    let mut ser = s.serialize_seq(Some(attributes.len()))?;
61
62    let mut sorted_flagged_attributes = attributes.to_owned();
63    sorted_flagged_attributes.sort();
64    for attr in sorted_flagged_attributes {
65        ser.serialize_element(&attr)?;
66    }
67    ser.end()
68}
69
70#[derive(SAD, Serialize, Deserialize, Debug, Clone)]
71pub struct LabelOverlay {
72    #[said]
73    #[serde(rename = "d")]
74    said: Option<said::SelfAddressingIdentifier>,
75    capture_base: Option<said::SelfAddressingIdentifier>,
76    #[serde(rename = "type")]
77    overlay_type: OverlayType,
78    language: Language,
79    #[serde(serialize_with = "serialize_categories")]
80    pub attribute_categories: Vec<String>, // TODO find out if we need duplicated structure to hold keys if we have hashmap with those keys
81    #[serde(serialize_with = "serialize_labels")]
82    pub attribute_labels: HashMap<String, String>,
83    #[serde(serialize_with = "serialize_labels")]
84    pub category_labels: HashMap<String, String>,
85    #[serde(skip)]
86    pub _category_attributes: HashMap<String, Vec<String>>,
87}
88
89impl Overlay for LabelOverlay {
90    fn as_any(&self) -> &dyn Any {
91        self
92    }
93    fn capture_base(&self) -> &Option<said::SelfAddressingIdentifier> {
94        &self.capture_base
95    }
96    fn set_capture_base(&mut self, said: &said::SelfAddressingIdentifier) {
97        self.capture_base = Some(said.clone());
98    }
99    fn overlay_type(&self) -> &OverlayType {
100        &self.overlay_type
101    }
102    fn said(&self) -> &Option<said::SelfAddressingIdentifier> {
103        &self.said
104    }
105    fn language(&self) -> Option<&Language> {
106        Some(&self.language)
107    }
108    fn attributes(&self) -> Vec<&String> {
109        self.attribute_labels.keys().collect::<Vec<&String>>()
110    }
111    /// Add an attribute to the Label Overlay
112    /// TODO add assignment of attribute to category
113    fn add(&mut self, attribute: &Attribute) {
114        if let Some(labels) = &attribute.labels {
115            if let Some(value) = labels.get(&self.language) {
116                self.attribute_labels
117                    .insert(attribute.name.clone(), value.to_string());
118            }
119        }
120        if let Some(category_labels) = &attribute.category_labels {
121            if let Some(value) = category_labels.get(&self.language) {
122                self.category_labels
123                    .insert(attribute.name.clone(), value.to_string());
124            }
125        }
126    }
127}
128
129impl LabelOverlay {
130    pub fn new(lang: Language) -> Self {
131        let overlay_version = "1.1".to_string();
132        Self {
133            capture_base: None,
134            said: None,
135            overlay_type: OverlayType::Label(overlay_version),
136            language: lang,
137            attribute_labels: HashMap::new(),
138            attribute_categories: vec![],
139            category_labels: HashMap::new(),
140            _category_attributes: HashMap::new(),
141        }
142    }
143
144    fn _add_to_category(&mut self, categories: Vec<&str>, attribute: &Attribute) {
145        let mut supercats: Vec<i32> = vec![];
146        for (i, category) in categories.iter().enumerate() {
147            let supercats_str: Vec<String> = supercats.iter().map(|c| c.to_string()).collect();
148            let mut supercat = String::new();
149            if !supercats_str.is_empty() {
150                supercat = format!("-{}", supercats_str.join("-"))
151            }
152            let regex = regex::Regex::new(format!("^_cat{supercat}(-[0-9]*)_$").as_str()).unwrap();
153            let mut acctual_cat_id = String::new();
154            let mut category_exists = false;
155            for (cat_id, cat_label) in self.category_labels.iter() {
156                if cat_label == category && regex.is_match(cat_id) {
157                    let cat_temp = cat_id.replace('_', "");
158                    let mut temp = cat_temp.split('-').collect::<Vec<&str>>();
159                    temp.remove(0);
160                    supercats = temp.iter().map(|c| c.parse::<i32>().unwrap()).collect();
161                    acctual_cat_id = cat_id.to_string();
162                    category_exists = true;
163                }
164            }
165
166            if !category_exists {
167                let mut count = 0;
168                for cat in self.attribute_categories.iter() {
169                    if regex.is_match(cat.as_str()) {
170                        count += 1;
171                    }
172                }
173                acctual_cat_id = format!("_cat{}-{}_", supercat, count + 1);
174                supercats.push(count + 1);
175                self.category_labels
176                    .insert(acctual_cat_id.clone(), category.to_string());
177                self.attribute_categories.push(acctual_cat_id.clone());
178                self._category_attributes
179                    .insert(acctual_cat_id.clone(), vec![]);
180            }
181
182            if i + 1 == categories.len() {
183                self._category_attributes
184                    .get_mut(acctual_cat_id.as_str())
185                    .unwrap()
186                    .push(attribute.name.clone());
187            }
188        }
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195
196    #[test]
197    fn create_label_overlay() {
198        let mut overlay = LabelOverlay::new(Language::Eng);
199        let attr = cascade! {
200            Attribute::new("attr1".to_string());
201            ..set_label(Language::Pol, "Etykieta".to_string());
202            ..set_label(Language::Eng, "Label".to_string());
203            ..add_category_label(Language::Eng, "Category".to_string());
204            ..add_category_label(Language::Pol, "Kategoria".to_string());
205        };
206        // even that attribute has 2 lagnuage only one attribute should be added to the overlay according to it's language
207        overlay.add(&attr);
208
209        let overlay_version = "1.1".to_string();
210        assert_eq!(overlay.overlay_type, OverlayType::Label(overlay_version));
211        assert_eq!(overlay.language, Language::Eng);
212        assert_eq!(overlay.attribute_labels.len(), 1);
213        assert_eq!(overlay.category_labels.len(), 1);
214    }
215    #[test]
216    #[ignore = "not implemented"]
217    fn resolve_categories_from_label() {
218        let mut overlay = LabelOverlay::new(Language::Eng);
219        let attr = cascade! {
220            Attribute::new("attr1".to_string());
221            ..set_label(Language::Pol, "Label 1".to_string());
222            ..add_category_label(Language::Eng, "Cat 1".to_string());
223        };
224        overlay.add(&attr);
225        let attr = cascade! {
226            Attribute::new("attr2".to_string());
227            ..set_label(Language::Pol, "Label 2".to_string());
228            ..add_category_label(Language::Eng, "Cat 2".to_string());
229        };
230        overlay.add(&attr);
231
232        assert_eq!(overlay.category_labels.len(), 2);
233        assert!(overlay
234            .attribute_categories
235            .contains(&"_cat-1_".to_string()));
236        assert!(overlay
237            .attribute_categories
238            .contains(&"_cat-2_".to_string()));
239
240        assert!(overlay.category_labels.contains_key("_cat-1_"));
241        if let Some(cat1) = overlay.category_labels.get("_cat-1_") {
242            assert_eq!(*cat1, "Cat 1".to_string());
243        }
244        assert!(overlay.category_labels.contains_key("_cat-2_"));
245        if let Some(cat2) = overlay.category_labels.get("_cat-2_") {
246            assert_eq!(*cat2, "Cat 2".to_string());
247        }
248
249        assert!(overlay._category_attributes.contains_key("_cat-1_"));
250        if let Some(cat1_attrs) = overlay._category_attributes.get("_cat-1_") {
251            assert_eq!(cat1_attrs.len(), 1);
252            assert!(cat1_attrs.contains(&"attr1".to_string()));
253        }
254        assert!(overlay._category_attributes.contains_key("_cat-2_"));
255        if let Some(cat2_attrs) = overlay._category_attributes.get("_cat-2_") {
256            assert_eq!(cat2_attrs.len(), 1);
257            assert!(cat2_attrs.contains(&"attr2".to_string()));
258        }
259    }
260}