oca_bundle/state/oca/overlay/
label.rs

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