oca_bundle/state/oca/overlay/
label.rs1use 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>, #[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 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 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}