oca_bundle/state/
oca.rs

1use crate::state::oca::layout::credential::Layout as CredentialLayout;
2use crate::state::oca::layout::form::Layout as FormLayout;
3use crate::state::oca::overlay::cardinality::Cardinalitys;
4use crate::state::oca::overlay::character_encoding::CharacterEncodings;
5use crate::state::oca::overlay::conditional::Conditionals;
6use crate::state::oca::overlay::conformance::Conformances;
7use crate::state::oca::overlay::entry::Entries;
8use crate::state::oca::overlay::entry_code::EntryCodes;
9#[cfg(feature = "format_overlay")]
10use crate::state::oca::overlay::format::Formats;
11use crate::state::oca::overlay::information::Information;
12use crate::state::oca::overlay::label::Labels;
13use crate::state::oca::overlay::meta::Metas;
14use crate::state::oca::overlay::unit::{AttributeUnit, Unit};
15use indexmap::IndexMap;
16use linked_hash_map::LinkedHashMap;
17use said::sad::{SerializationFormats, SAD};
18use said::version::SerializationInfo;
19use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
20use std::collections::HashMap;
21pub mod capture_base;
22mod layout;
23pub mod overlay;
24use crate::state::{
25    attribute::Attribute,
26    oca::{capture_base::CaptureBase, overlay::Overlay},
27};
28use convert_case::{Case, Casing};
29use isolang::Language;
30use oca_ast::ast::{
31    CaptureContent, Command, CommandType, Content, NestedValue, OCAAst, ObjectKind, OverlayType,
32};
33/// Internal representation of OCA objects in split between non-attributes values and attributes.
34/// It is used to build dynamically objects without knowing yet whole structure of the object.
35/// Used mainly as a container to hold information while parsing OCAfile.
36/// Example of usage:
37///
38/// let oca = OCABox::new()
39/// let attr = Attribute::new("name")
40/// oca.add_attribute(attr)
41/// oca.get_attribute_by_name("name").setEncoding(Encoding::UTF8)
42/// oca.get_attribute_by_name("name").setLabel(Language::English, "Name")
43/// oca.get_attribute_by_name("name").setInformation(Language::German, "Name")
44/// oca.get_attribute_by_name("name").setUnit("kg")
45/// oca.get_attribute_by_name("name").setStandard("ISO 1234")
46/// oca.get_attribute_by_name("name").setCategory("personal")
47/// oca.generate_bundle().unwrap()
48///
49///
50/// TODO:
51/// How to add multiple overlays like mapping or layout (how to identify them?)
52
53#[derive(Clone)]
54pub struct OCABox {
55    pub attributes: HashMap<String, Attribute>,
56    pub credential_layouts: Option<Vec<CredentialLayout>>,
57    pub form_layouts: Option<Vec<FormLayout>>,
58    pub mappings: Option<Vec<overlay::AttributeMapping>>,
59    pub meta: Option<HashMap<Language, HashMap<String, String>>>,
60    pub classification: Option<String>,
61}
62
63impl Default for OCABox {
64    fn default() -> Self {
65        Self::new()
66    }
67}
68
69impl OCABox {
70    pub fn new() -> Self {
71        OCABox {
72            attributes: HashMap::new(),
73            credential_layouts: None,
74            form_layouts: None,
75            mappings: None,
76            meta: None,
77            classification: None,
78        }
79    }
80    /// Remove attribute from the OCA Bundle
81    /// if attribute does not exist, nothing will happen
82    pub fn remove_attribute(&mut self, attr_name: &String) {
83        self.attributes.remove(attr_name);
84    }
85    /// Add an attribute to the OCA Bundle
86    /// If the attribute already exists, it will be merged with the new attribute
87    /// for simple types: the new value will overwrite the old value
88    /// for complex types: the new value will be added to the old value
89    pub fn add_attribute(&mut self, attribute: Attribute) {
90        if let Some(attr) = self.get_attribute_mut(&attribute.name) {
91            attr.merge(&attribute);
92        } else {
93            self.attributes.insert(attribute.name.clone(), attribute);
94        }
95    }
96    pub fn get_attribute_by_name(&self, name: &str) -> Option<&Attribute> {
97        self.attributes.get(name)
98    }
99
100    pub fn add_attribute_mapping(&mut self, mapping: overlay::AttributeMapping) {
101        match self.mappings {
102            Some(ref mut mappings) => mappings.push(mapping),
103            None => self.mappings = Some(vec![mapping]),
104        }
105    }
106    pub fn add_classification(&mut self, classification: String) {
107        self.classification = Some(classification);
108    }
109
110    pub fn remove_classification(&mut self) {
111        self.classification = None;
112    }
113
114    pub fn generate_bundle(&mut self) -> OCABundle {
115        let mut capture_base = self.generate_capture_base();
116        let mut overlays = self.generate_overlays();
117
118        capture_base.sign();
119
120        let cb_said = capture_base.said.as_ref();
121        overlays.iter_mut().for_each(|x| x.sign(cb_said.unwrap()));
122
123        let mut oca_bundle = OCABundle {
124            said: None,
125            capture_base,
126            overlays,
127        };
128
129        oca_bundle.compute_digest();
130        oca_bundle
131    }
132
133    fn generate_overlays(&mut self) -> Vec<DynOverlay> {
134        let mut overlays: Vec<DynOverlay> = Vec::new();
135        if let Some(mappings) = &self.mappings {
136            for mapping in mappings {
137                overlays.push(Box::new(mapping.clone()));
138            }
139        }
140        if let Some(meta) = &self.meta {
141            for (lang, attr_pairs) in meta {
142                let meta_ov = overlay::Meta::new(*lang, attr_pairs.clone());
143                overlays.push(Box::new(meta_ov));
144            }
145        }
146        if let Some(layouts) = &self.form_layouts {
147            for layout in layouts {
148                let layout_ov = overlay::FormLayout::new(layout.clone());
149                overlays.push(Box::new(layout_ov));
150            }
151        }
152        if let Some(layouts) = &self.credential_layouts {
153            for layout in layouts {
154                let layout_ov = overlay::CredentialLayout::new(layout.clone());
155                overlays.push(Box::new(layout_ov));
156            }
157        }
158
159        for attribute in self.attributes.values() {
160            if attribute.encoding.is_some() {
161                let mut encoding_ov = overlays
162                    .iter_mut()
163                    .find(|x| x.overlay_type().eq(&OverlayType::CharacterEncoding));
164                if encoding_ov.is_none() {
165                    overlays.push(Box::new(overlay::CharacterEncoding::new()));
166                    encoding_ov = overlays.last_mut();
167                }
168                if let Some(ov) = encoding_ov {
169                    ov.add(attribute);
170                }
171            }
172
173            #[cfg(feature = "format_overlay")]
174            if attribute.format.is_some() {
175                let mut format_ov = overlays
176                    .iter_mut()
177                    .find(|x| x.overlay_type().eq(&OverlayType::Format));
178                if format_ov.is_none() {
179                    overlays.push(Box::new(overlay::Format::new()));
180                    format_ov = overlays.last_mut();
181                }
182                if let Some(ov) = format_ov {
183                    ov.add(attribute);
184                }
185            }
186
187            if attribute.conformance.is_some() {
188                let mut conformance_ov = overlays
189                    .iter_mut()
190                    .find(|x| x.overlay_type().eq(&OverlayType::Conformance));
191                if conformance_ov.is_none() {
192                    overlays.push(Box::new(overlay::Conformance::new()));
193                    conformance_ov = overlays.last_mut();
194                }
195                if let Some(ov) = conformance_ov {
196                    ov.add(attribute);
197                }
198            }
199
200            if attribute.cardinality.is_some() {
201                let mut cardinality_ov = overlays
202                    .iter_mut()
203                    .find(|x| x.overlay_type().eq(&OverlayType::Cardinality));
204                if cardinality_ov.is_none() {
205                    overlays.push(Box::new(overlay::Cardinality::new()));
206                    cardinality_ov = overlays.last_mut();
207                }
208                if let Some(ov) = cardinality_ov {
209                    ov.add(attribute);
210                }
211            }
212
213            if attribute.condition.is_some() {
214                let mut conditional_ov = overlays
215                    .iter_mut()
216                    .find(|x| x.overlay_type().eq(&OverlayType::Conditional));
217                if conditional_ov.is_none() {
218                    overlays.push(Box::new(overlay::Conditional::new()));
219                    conditional_ov = overlays.last_mut();
220                }
221                if let Some(ov) = conditional_ov {
222                    ov.add(attribute);
223                }
224            }
225
226            if let Some(units) = &attribute.units {
227                for measurement_system in units.keys() {
228                    let mut unit_ov = overlays.iter_mut().find(|x| {
229                        if let Some(x_unit) = x.as_any().downcast_ref::<overlay::Unit>() {
230                            x_unit.measurement_system() == Some(measurement_system)
231                        } else {
232                            false
233                        }
234                    });
235                    if unit_ov.is_none() {
236                        overlays.push(Box::new(overlay::Unit::new(measurement_system.clone())));
237                        unit_ov = overlays.last_mut();
238                    }
239                    if let Some(ov) = unit_ov {
240                        ov.add(attribute);
241                    }
242                }
243            }
244
245            if attribute.entry_codes.is_some() {
246                let mut entry_code_ov = overlays
247                    .iter_mut()
248                    .find(|x| x.overlay_type().eq(&OverlayType::EntryCode));
249                if entry_code_ov.is_none() {
250                    overlays.push(Box::new(overlay::EntryCode::new()));
251                    entry_code_ov = overlays.last_mut();
252                }
253                if let Some(ov) = entry_code_ov {
254                    ov.add(attribute);
255                }
256            }
257
258            if let Some(entries) = &attribute.entries {
259                for lang in entries.keys() {
260                    let mut entry_ov = overlays.iter_mut().find(|x| {
261                        x.overlay_type().eq(&OverlayType::Entry) && x.language() == Some(lang)
262                    });
263                    if entry_ov.is_none() {
264                        overlays.push(Box::new(overlay::Entry::new(*lang)));
265                        entry_ov = overlays.last_mut();
266                    }
267                    if let Some(ov) = entry_ov {
268                        ov.add(attribute);
269                    }
270                }
271            }
272
273            if let Some(labels) = &attribute.labels {
274                for lang in labels.keys() {
275                    let mut label_ov = overlays.iter_mut().find(|x| {
276                        x.overlay_type().eq(&OverlayType::Label) && x.language() == Some(lang)
277                    });
278                    if label_ov.is_none() {
279                        overlays.push(Box::new(overlay::Label::new(*lang)));
280                        label_ov = overlays.last_mut();
281                    }
282                    if let Some(ov) = label_ov {
283                        ov.add(attribute);
284                    }
285                }
286            }
287
288            if let Some(information) = &attribute.informations {
289                for lang in information.keys() {
290                    let mut info_ov = overlays.iter_mut().find(|x| {
291                        x.overlay_type().eq(&OverlayType::Information) && x.language() == Some(lang)
292                    });
293                    if info_ov.is_none() {
294                        overlays.push(Box::new(overlay::Information::new(*lang)));
295                        info_ov = overlays.last_mut();
296                    }
297                    if let Some(ov) = info_ov {
298                        ov.add(attribute);
299                    }
300                }
301            }
302        }
303
304        overlays
305    }
306    fn generate_capture_base(&mut self) -> CaptureBase {
307        let mut capture_base = CaptureBase::new();
308        if let Some(classification) = &self.classification {
309            capture_base.set_classification(classification);
310        }
311        for attribute in self.attributes.values() {
312            capture_base.add(attribute);
313        }
314        capture_base
315    }
316    fn get_attribute_mut(&mut self, name: &str) -> Option<&mut Attribute> {
317        self.attributes.get_mut(name)
318    }
319}
320
321pub type DynOverlay = Box<dyn Overlay + Send + Sync + 'static>;
322
323impl<'de> Deserialize<'de> for DynOverlay {
324    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
325    where
326        D: Deserializer<'de>,
327    {
328        let de_overlay = serde_value::Value::deserialize(deserializer)?;
329        if let serde_value::Value::Map(ref overlay) = de_overlay {
330            if let Some(de_overlay_type) =
331                overlay.get(&serde_value::Value::String("type".to_string()))
332            {
333                let overlay_type = de_overlay_type
334                    .clone()
335                    .deserialize_into::<OverlayType>()
336                    .map_err(|e| serde::de::Error::custom(format!("Overlay type: {e}")))?;
337
338                match overlay_type {
339                    OverlayType::AttributeMapping => {
340                        return Ok(Box::new(
341                            de_overlay
342                                .deserialize_into::<overlay::AttributeMapping>()
343                                .map_err(|e| {
344                                    serde::de::Error::custom(format!(
345                                        "Attribute Mapping overlay: {e}"
346                                    ))
347                                })?,
348                        ));
349                    }
350                    OverlayType::CharacterEncoding => {
351                        return Ok(Box::new(
352                            de_overlay
353                                .deserialize_into::<overlay::CharacterEncoding>()
354                                .map_err(|e| {
355                                    serde::de::Error::custom(format!(
356                                        "Character Encoding overlay: {e}"
357                                    ))
358                                })?,
359                        ));
360                    }
361                    OverlayType::Cardinality => {
362                        return Ok(Box::new(
363                            de_overlay
364                                .deserialize_into::<overlay::Cardinality>()
365                                .map_err(|e| {
366                                    serde::de::Error::custom(format!("Cardinality overlay: {e}"))
367                                })?,
368                        ));
369                    }
370                    OverlayType::Conformance => {
371                        return Ok(Box::new(
372                            de_overlay
373                                .deserialize_into::<overlay::Conformance>()
374                                .map_err(|e| {
375                                    serde::de::Error::custom(format!("Conformance overlay: {e}"))
376                                })?,
377                        ));
378                    }
379                    OverlayType::Conditional => {
380                        return Ok(Box::new(
381                            de_overlay
382                                .deserialize_into::<overlay::Conditional>()
383                                .map_err(|e| {
384                                    serde::de::Error::custom(format!("Conditional overlay: {e}"))
385                                })?,
386                        ));
387                    }
388                    OverlayType::Entry => {
389                        return Ok(Box::new(
390                            de_overlay
391                                .deserialize_into::<overlay::Entry>()
392                                .map_err(|e| {
393                                    serde::de::Error::custom(format!("Entry overlay: {e}"))
394                                })?,
395                        ));
396                    }
397                    OverlayType::EntryCode => {
398                        return Ok(Box::new(
399                            de_overlay
400                                .deserialize_into::<overlay::EntryCode>()
401                                .map_err(|e| {
402                                    serde::de::Error::custom(format!("Entry Code overlay: {e}"))
403                                })?,
404                        ));
405                    }
406                    OverlayType::EntryCodeMapping => {
407                        return Ok(Box::new(
408                            de_overlay
409                                .deserialize_into::<overlay::EntryCodeMapping>()
410                                .map_err(|e| {
411                                    serde::de::Error::custom(format!(
412                                        "Entry Code Mapping overlay: {e}"
413                                    ))
414                                })?,
415                        ));
416                    }
417                    OverlayType::Unit => {
418                        return Ok(Box::new(
419                            de_overlay
420                                .deserialize_into::<overlay::Unit>()
421                                .map_err(|e| {
422                                    serde::de::Error::custom(format!("Unit overlay: {e}"))
423                                })?,
424                        ));
425                    }
426
427                    #[cfg(feature = "format_overlay")]
428                    OverlayType::Format => {
429                        return Ok(Box::new(
430                            de_overlay
431                                .deserialize_into::<overlay::Format>()
432                                .map_err(|e| {
433                                    serde::de::Error::custom(format!("Format overlay: {e}"))
434                                })?,
435                        ));
436                    }
437
438                    OverlayType::Information => {
439                        return Ok(Box::new(
440                            de_overlay
441                                .deserialize_into::<overlay::Information>()
442                                .map_err(|e| {
443                                    serde::de::Error::custom(format!("Information overlay: {e}"))
444                                })?,
445                        ));
446                    }
447                    OverlayType::Label => {
448                        return Ok(Box::new(
449                            de_overlay
450                                .deserialize_into::<overlay::Label>()
451                                .map_err(|e| {
452                                    serde::de::Error::custom(format!("Label overlay: {e}"))
453                                })?,
454                        ));
455                    }
456                    OverlayType::Meta => {
457                        return Ok(Box::new(
458                            de_overlay
459                                .deserialize_into::<overlay::Meta>()
460                                .map_err(|e| {
461                                    serde::de::Error::custom(format!("Meta overlay: {e}"))
462                                })?,
463                        ));
464                    }
465
466                    /* OverlayType::FormLayout => {
467                        return Ok(Box::new(
468                            de_overlay
469                                .deserialize_into::<overlay::FormLayout>()
470                                .map_err(|e| {
471                                    serde::de::Error::custom(format!("Form Layout overlay: {e}"))
472                                })?,
473                        ));
474                    }
475                    OverlayType::CredentialLayout => {
476                        return Ok(Box::new(
477                            de_overlay
478                                .deserialize_into::<overlay::CredentialLayout>()
479                                .map_err(|e| {
480                                    serde::de::Error::custom(format!("Credential Layout overlay: {e}"))
481                                })?,
482                        ));
483                    } */
484                    OverlayType::Subset => {
485                        return Ok(Box::new(
486                            de_overlay
487                                .deserialize_into::<overlay::Subset>()
488                                .map_err(|e| {
489                                    serde::de::Error::custom(format!("Subset overlay: {e}"))
490                                })?,
491                        ));
492                    }
493                    OverlayType::Standard => {
494                        return Ok(Box::new(
495                            de_overlay
496                                .deserialize_into::<overlay::Standard>()
497                                .map_err(|e| {
498                                    serde::de::Error::custom(format!("Standard overlay: {e}"))
499                                })?,
500                        ));
501                    }
502                    _ => {
503                        return Err(serde::de::Error::custom(format!(
504                            "Overlay type not supported: {:?}",
505                            overlay_type
506                        )));
507                    }
508                }
509            } else {
510                return Err(serde::de::Error::missing_field("type"));
511            }
512        }
513
514        Err(serde::de::Error::custom(format!(
515            "overlay must be an object, got: {de_overlay:?}"
516        )))
517    }
518}
519
520pub fn serialize_overlays<S>(overlays: &Vec<DynOverlay>, s: S) -> Result<S::Ok, S::Error>
521where
522    S: Serializer,
523{
524    use serde_value::Value;
525    #[derive(Serialize)]
526    #[serde(untagged)]
527    enum OverlayValue {
528        Array(Vec<DynOverlay>),
529        Object(Box<dyn Overlay + Send>),
530    }
531
532    let mut overlays_map: LinkedHashMap<Value, OverlayValue> = LinkedHashMap::new();
533    let overlays_order = [
534        OverlayType::CharacterEncoding,
535        OverlayType::Format,
536        OverlayType::Meta,
537        OverlayType::Label,
538        OverlayType::Information,
539        OverlayType::Standard,
540        OverlayType::Conditional,
541        OverlayType::Conformance,
542        OverlayType::EntryCode,
543        OverlayType::Entry,
544        OverlayType::Cardinality,
545        OverlayType::Unit,
546        OverlayType::AttributeMapping,
547        OverlayType::EntryCodeMapping,
548        OverlayType::UnitMapping,
549        OverlayType::Subset,
550        /* OverlayType::CredentialLayout,
551        OverlayType::FormLayout, */
552    ];
553    for o_type in overlays_order {
554        for overlay in overlays {
555            let o_type_str = o_type.to_string().to_case(Case::Snake);
556            if overlay.overlay_type().eq(&o_type) {
557                match overlay.language() {
558                    Some(_) => {
559                        if let Some(OverlayValue::Array(ov)) =
560                            overlays_map.get_mut(&Value::String(o_type_str.clone()))
561                        {
562                            ov.push(overlay.clone());
563                        } else {
564                            overlays_map.insert(
565                                Value::String(o_type_str.clone()),
566                                OverlayValue::Array(vec![overlay.clone()]),
567                            );
568                        }
569                    }
570                    None => {
571                        overlays_map.insert(
572                            Value::String(o_type_str),
573                            OverlayValue::Object(overlay.clone()),
574                        );
575                    }
576                }
577            }
578        }
579    }
580
581    let mut ser = s.serialize_map(Some(overlays_map.len()))?;
582    for (ov_type, v) in overlays_map.iter_mut() {
583        if let OverlayValue::Array(ov) = v {
584            ov.sort_by(|a, b| {
585                if let Some(a_lang) = a.language() {
586                    if let Some(b_lang) = b.language() {
587                        a_lang.cmp(b_lang)
588                    } else {
589                        std::cmp::Ordering::Equal
590                    }
591                } else {
592                    std::cmp::Ordering::Equal
593                }
594            });
595        }
596
597        ser.serialize_entry(ov_type, v)?;
598    }
599    ser.end()
600}
601
602fn deserialize_overlays<'de, D>(deserializer: D) -> Result<Vec<DynOverlay>, D::Error>
603where
604    D: serde::de::Deserializer<'de>,
605{
606    struct OverlaysVisitor;
607
608    impl<'de> serde::de::Visitor<'de> for OverlaysVisitor {
609        type Value = Vec<DynOverlay>;
610
611        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
612            formatter.write_str("vector of overlays")
613        }
614
615        fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
616        where
617            V: serde::de::MapAccess<'de>,
618        {
619            let mut overlays = vec![];
620
621            while let Some((_, value)) = map.next_entry::<String, serde_value::Value>()? {
622                if let serde_value::Value::Seq(ov) = value {
623                    for o in ov {
624                        overlays.push(o.deserialize_into().unwrap());
625                    }
626                } else if let serde_value::Value::Map(_) = value {
627                    overlays.push(value.deserialize_into().unwrap());
628                }
629            }
630
631            Ok(overlays)
632        }
633    }
634
635    deserializer.deserialize_any(OverlaysVisitor)
636}
637
638impl std::fmt::Debug for DynOverlay {
639    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
640        write!(
641            f,
642            "DynOverlay {{ overlay_type: {}, attributes: {:?} }}",
643            self.overlay_type(),
644            self.attributes()
645        )
646    }
647}
648
649#[derive(SAD, Serialize, Debug, Deserialize, Clone)]
650#[version(protocol = "OCAB", major = 1, minor = 0)]
651// #[said(format = "JSON")]
652pub struct OCABundle {
653    #[said]
654    #[serde(rename = "d")]
655    pub said: Option<said::SelfAddressingIdentifier>,
656    pub capture_base: CaptureBase,
657    #[serde(
658        serialize_with = "serialize_overlays",
659        deserialize_with = "deserialize_overlays"
660    )]
661    pub overlays: Vec<DynOverlay>,
662}
663
664impl From<OCABundle> for OCABox {
665    fn from(oca_bundle: OCABundle) -> Self {
666        let mut oca_box = OCABox::new();
667        oca_box.add_classification(oca_bundle.capture_base.classification);
668
669        let mut attributes: HashMap<String, Attribute> = HashMap::new();
670        for (attr_name, attr_type) in oca_bundle.capture_base.attributes {
671            let attr = Attribute {
672                name: attr_name.clone(),
673                attribute_type: Some(attr_type),
674                // TODO find out how to make sure that said or Array said would be in a attr type
675                // reference_sai: ref_said,
676                ..Default::default()
677            };
678            attributes.insert(attr_name.clone(), attr);
679        }
680        for attr_name in oca_bundle.capture_base.flagged_attributes {
681            attributes.get_mut(&attr_name).unwrap().set_flagged();
682        }
683
684        let meta_overlays = oca_bundle
685            .overlays
686            .iter()
687            .filter_map(|x| x.as_any().downcast_ref::<overlay::Meta>())
688            .collect::<Vec<_>>();
689        for overlay in meta_overlays {
690            for (meta_name, meta_value) in overlay.attr_pairs.iter() {
691                oca_box.add_meta(
692                    *overlay.language().unwrap(),
693                    meta_name.clone(),
694                    meta_value.clone(),
695                );
696            }
697        }
698
699        let character_encoding_overlays = oca_bundle
700            .overlays
701            .iter()
702            .filter_map(|x| x.as_any().downcast_ref::<overlay::CharacterEncoding>())
703            .collect::<Vec<_>>();
704        for overlay in character_encoding_overlays {
705            for (attr_name, encoding) in overlay.attribute_character_encoding.iter() {
706                attributes
707                    .get_mut(attr_name)
708                    .unwrap()
709                    .set_encoding(*encoding);
710            }
711        }
712
713        let conformance_overlays = oca_bundle
714            .overlays
715            .iter()
716            .filter_map(|x| x.as_any().downcast_ref::<overlay::Conformance>())
717            .collect::<Vec<_>>();
718        for overlay in conformance_overlays {
719            for (attr_name, conformance) in overlay.attribute_conformance.iter() {
720                attributes
721                    .get_mut(attr_name)
722                    .unwrap()
723                    .set_conformance(conformance.clone());
724            }
725        }
726
727        let conditional_overlays = oca_bundle
728            .overlays
729            .iter()
730            .filter_map(|x| x.as_any().downcast_ref::<overlay::Conditional>())
731            .collect::<Vec<_>>();
732        for overlay in conditional_overlays {
733            let re = regex::Regex::new(r"\$\{(\d+)\}").unwrap();
734
735            for (attr_name, condition) in overlay.attribute_conditions.iter() {
736                let condition_dependencies = overlay.attribute_dependencies.get(attr_name).unwrap(); // todo
737                let cond = re
738                    .replace_all(condition, |caps: &regex::Captures| {
739                        let dep = condition_dependencies[caps[1].parse::<usize>().unwrap()].clone();
740                        format!("${{{}}}", dep)
741                    })
742                    .to_string();
743
744                attributes
745                    .get_mut(attr_name)
746                    .unwrap()
747                    .set_condition(cond.clone());
748            }
749        }
750
751        let cardinality_overlays = oca_bundle
752            .overlays
753            .iter()
754            .filter_map(|x| x.as_any().downcast_ref::<overlay::Cardinality>())
755            .collect::<Vec<_>>();
756        for overlay in cardinality_overlays {
757            for (attr_name, cardinality) in overlay.attribute_cardinality.iter() {
758                attributes
759                    .get_mut(attr_name)
760                    .unwrap()
761                    .set_cardinality(cardinality.clone());
762            }
763        }
764
765        #[cfg(feature = "format_overlay")]
766        {
767            let format_overlays = oca_bundle
768                .overlays
769                .iter()
770                .filter_map(|x| x.as_any().downcast_ref::<overlay::Format>())
771                .collect::<Vec<_>>();
772            for overlay in format_overlays {
773                for (attr_name, format) in overlay.attribute_formats.iter() {
774                    attributes
775                        .get_mut(attr_name)
776                        .unwrap()
777                        .set_format(format.clone());
778                }
779            }
780        }
781
782        let unit_overlays = oca_bundle
783            .overlays
784            .iter()
785            .filter_map(|x| x.as_any().downcast_ref::<overlay::Unit>())
786            .collect::<Vec<_>>();
787        for overlay in unit_overlays {
788            for (attr_name, unit) in overlay.attribute_units.iter() {
789                attributes
790                    .get_mut(attr_name)
791                    .unwrap()
792                    .set_unit(AttributeUnit {
793                        measurement_system: overlay.measurement_system().unwrap().clone(),
794                        unit: unit.clone(),
795                    });
796            }
797        }
798
799        let entry_code_overlays = oca_bundle
800            .overlays
801            .iter()
802            .filter_map(|x| x.as_any().downcast_ref::<overlay::EntryCode>())
803            .collect::<Vec<_>>();
804        for overlay in entry_code_overlays {
805            for (attr_name, entry_code) in overlay.attribute_entry_codes.iter() {
806                attributes
807                    .get_mut(attr_name)
808                    .unwrap()
809                    .set_entry_codes(entry_code.clone());
810            }
811        }
812
813        let entry_overlays = oca_bundle
814            .overlays
815            .iter()
816            .filter_map(|x| x.as_any().downcast_ref::<overlay::Entry>())
817            .collect::<Vec<_>>();
818        for overlay in entry_overlays {
819            for (attr_name, entries) in overlay.attribute_entries.iter() {
820                attributes
821                    .get_mut(attr_name)
822                    .unwrap()
823                    .set_entry(*overlay.language().unwrap(), entries.clone());
824            }
825        }
826
827        let label_overlays = oca_bundle
828            .overlays
829            .iter()
830            .filter_map(|x| x.as_any().downcast_ref::<overlay::Label>())
831            .collect::<Vec<_>>();
832        for overlay in label_overlays {
833            for (attr_name, label) in overlay.attribute_labels.iter() {
834                attributes
835                    .get_mut(attr_name)
836                    .unwrap()
837                    .set_label(*overlay.language().unwrap(), label.clone());
838            }
839        }
840
841        let information_overlays = oca_bundle
842            .overlays
843            .iter()
844            .filter_map(|x| x.as_any().downcast_ref::<overlay::Information>())
845            .collect::<Vec<_>>();
846        for overlay in information_overlays {
847            for (attr_name, information) in overlay.attribute_information.iter() {
848                attributes
849                    .get_mut(attr_name)
850                    .unwrap()
851                    .set_information(*overlay.language().unwrap(), information.clone());
852            }
853        }
854
855        for (_, attribute) in attributes {
856            oca_box.add_attribute(attribute);
857        }
858
859        oca_box
860    }
861}
862
863impl OCABundle {
864    pub fn fill_said(&mut self) {
865        self.compute_digest();
866    }
867
868    pub fn to_ast(&self) -> OCAAst {
869        let mut ast = OCAAst::new();
870
871        let mut properties = None;
872        if !self.capture_base.classification.is_empty() {
873            properties = Some(IndexMap::new());
874            properties.as_mut().unwrap().insert(
875                "classification".to_string(),
876                NestedValue::Value(self.capture_base.classification.clone()),
877            );
878        }
879
880        let mut attributes = IndexMap::new();
881        self.capture_base
882            .attributes
883            .iter()
884            .for_each(|(attr_name, attr_type)| {
885                attributes.insert(attr_name.clone(), attr_type.clone());
886            });
887
888        let command = Command {
889            kind: CommandType::Add,
890            object_kind: ObjectKind::CaptureBase(CaptureContent {
891                // TODO find out if we can use indexmap in capture base to simplify stuff
892                attributes: Some(self.capture_base.attributes.clone().into_iter().collect()),
893                properties,
894                flagged_attributes: None,
895            }),
896        };
897        ast.commands.push(command);
898
899        self.overlays.iter().for_each(|overlay| {
900            match overlay.overlay_type() {
901                OverlayType::CharacterEncoding => {
902                    let character_encoding = overlay
903                        .as_any()
904                        .downcast_ref::<overlay::CharacterEncoding>()
905                        .unwrap();
906                    let mut attributes = IndexMap::new();
907                    for (attr_name, encoding) in
908                        character_encoding.attribute_character_encoding.iter()
909                    {
910                        let encoding_val = serde_json::to_value(encoding).unwrap();
911                        attributes.insert(
912                            attr_name.clone(),
913                            NestedValue::Value(encoding_val.as_str().unwrap().to_string()),
914                        );
915                    }
916                    let command = Command {
917                        kind: CommandType::Add,
918                        object_kind: ObjectKind::Overlay(
919                            OverlayType::CharacterEncoding,
920                            Content {
921                                attributes: Some(attributes),
922                                properties: None,
923                            },
924                        ),
925                    };
926                    ast.commands.push(command);
927                }
928                #[cfg(feature = "format_overlay")]
929                OverlayType::Format => {
930                    let format = overlay.as_any().downcast_ref::<overlay::Format>().unwrap();
931                    let mut attributes = IndexMap::new();
932                    for (attr_name, format) in format.attribute_formats.iter() {
933                        attributes.insert(attr_name.clone(), NestedValue::Value(format.clone()));
934                    }
935                    let command = Command {
936                        kind: CommandType::Add,
937                        object_kind: ObjectKind::Overlay(
938                            OverlayType::Format,
939                            Content {
940                                attributes: Some(attributes),
941                                properties: None,
942                            },
943                        ),
944                    };
945                    ast.commands.push(command);
946                }
947                OverlayType::Meta => {
948                    let meta = overlay.as_any().downcast_ref::<overlay::Meta>().unwrap();
949                    let mut properties = IndexMap::new();
950                    properties.insert(
951                        "lang".to_string(),
952                        NestedValue::Value(
953                            meta.language().unwrap().to_639_1().unwrap().to_string(),
954                        ),
955                    );
956                    for (meta_name, meta_value) in meta.attr_pairs.iter() {
957                        properties
958                            .insert(meta_name.clone(), NestedValue::Value(meta_value.clone()));
959                    }
960                    let command = Command {
961                        kind: CommandType::Add,
962                        object_kind: ObjectKind::Overlay(
963                            OverlayType::Meta,
964                            Content {
965                                attributes: None,
966                                properties: Some(properties),
967                            },
968                        ),
969                    };
970                    ast.commands.push(command);
971                }
972                OverlayType::Label => {
973                    let label = overlay.as_any().downcast_ref::<overlay::Label>().unwrap();
974                    let mut properties = IndexMap::new();
975                    properties.insert(
976                        "lang".to_string(),
977                        NestedValue::Value(
978                            label.language().unwrap().to_639_1().unwrap().to_string(),
979                        ),
980                    );
981                    let mut attributes = IndexMap::new();
982                    for (attr_name, label) in label.attribute_labels.iter() {
983                        attributes.insert(attr_name.clone(), NestedValue::Value(label.clone()));
984                    }
985                    let command = Command {
986                        kind: CommandType::Add,
987                        object_kind: ObjectKind::Overlay(
988                            OverlayType::Label,
989                            Content {
990                                attributes: Some(attributes),
991                                properties: Some(properties),
992                            },
993                        ),
994                    };
995                    ast.commands.push(command);
996                }
997                OverlayType::Information => {
998                    let information = overlay
999                        .as_any()
1000                        .downcast_ref::<overlay::Information>()
1001                        .unwrap();
1002                    let mut properties = IndexMap::new();
1003                    properties.insert(
1004                        "lang".to_string(),
1005                        NestedValue::Value(
1006                            information
1007                                .language()
1008                                .unwrap()
1009                                .to_639_1()
1010                                .unwrap()
1011                                .to_string(),
1012                        ),
1013                    );
1014                    let mut attributes = IndexMap::new();
1015                    for (attr_name, information) in information.attribute_information.iter() {
1016                        attributes
1017                            .insert(attr_name.clone(), NestedValue::Value(information.clone()));
1018                    }
1019                    let command = Command {
1020                        kind: CommandType::Add,
1021                        object_kind: ObjectKind::Overlay(
1022                            OverlayType::Information,
1023                            Content {
1024                                attributes: Some(attributes),
1025                                properties: Some(properties),
1026                            },
1027                        ),
1028                    };
1029                    ast.commands.push(command);
1030                }
1031                OverlayType::Conditional => {
1032                    let conditional = overlay
1033                        .as_any()
1034                        .downcast_ref::<overlay::Conditional>()
1035                        .unwrap();
1036                    let mut attributes = IndexMap::new();
1037                    let re = regex::Regex::new(r"\$\{(\d+)\}").unwrap();
1038                    for (attr_name, condition) in conditional.attribute_conditions.iter() {
1039                        let condition_dependencies =
1040                            conditional.attribute_dependencies.get(attr_name).unwrap(); // todo
1041                        let cond = re
1042                            .replace_all(condition, |caps: &regex::Captures| {
1043                                let dep = condition_dependencies[caps[1].parse::<usize>().unwrap()]
1044                                    .clone();
1045                                format!("${{{}}}", dep)
1046                            })
1047                            .to_string();
1048
1049                        attributes.insert(attr_name.clone(), NestedValue::Value(cond.clone()));
1050                    }
1051                    let command = Command {
1052                        kind: CommandType::Add,
1053                        object_kind: ObjectKind::Overlay(
1054                            OverlayType::Conditional,
1055                            Content {
1056                                attributes: Some(attributes),
1057                                properties: None,
1058                            },
1059                        ),
1060                    };
1061                    ast.commands.push(command);
1062                }
1063                OverlayType::Conformance => {
1064                    let conformance = overlay
1065                        .as_any()
1066                        .downcast_ref::<overlay::Conformance>()
1067                        .unwrap();
1068                    let mut attributes = IndexMap::new();
1069                    for (attr_name, conformance) in conformance.attribute_conformance.iter() {
1070                        attributes
1071                            .insert(attr_name.clone(), NestedValue::Value(conformance.clone()));
1072                    }
1073                    let command = Command {
1074                        kind: CommandType::Add,
1075                        object_kind: ObjectKind::Overlay(
1076                            OverlayType::Conformance,
1077                            Content {
1078                                attributes: Some(attributes),
1079                                properties: None,
1080                            },
1081                        ),
1082                    };
1083                    ast.commands.push(command);
1084                }
1085                OverlayType::EntryCode => {
1086                    let entry_code = overlay
1087                        .as_any()
1088                        .downcast_ref::<overlay::EntryCode>()
1089                        .unwrap();
1090                    let mut attributes = IndexMap::new();
1091                    for (attr_name, entry_code) in entry_code.attribute_entry_codes.iter() {
1092                        match entry_code {
1093                            crate::state::entry_codes::EntryCodes::Sai(said) => {
1094                                attributes.insert(
1095                                    attr_name.clone(),
1096                                    NestedValue::Value(said.to_string()),
1097                                );
1098                            }
1099                            crate::state::entry_codes::EntryCodes::Array(entry_codes) => {
1100                                attributes.insert(
1101                                    attr_name.clone(),
1102                                    NestedValue::Array(
1103                                        entry_codes
1104                                            .iter()
1105                                            .map(|code| NestedValue::Value(code.clone()))
1106                                            .collect(),
1107                                    ),
1108                                );
1109                            }
1110                            crate::state::entry_codes::EntryCodes::Object(grouped_entry_codes) => {
1111                                attributes.insert(
1112                                    attr_name.clone(),
1113                                    NestedValue::Object(
1114                                        grouped_entry_codes
1115                                            .iter()
1116                                            .map(|(k, v)| {
1117                                                let codes = v
1118                                                    .iter()
1119                                                    .map(|code| NestedValue::Value(code.clone()))
1120                                                    .collect();
1121                                                (k.clone(), NestedValue::Array(codes))
1122                                            })
1123                                            .collect(),
1124                                    ),
1125                                );
1126                            }
1127                        }
1128                    }
1129                    let command = Command {
1130                        kind: CommandType::Add,
1131                        object_kind: ObjectKind::Overlay(
1132                            OverlayType::EntryCode,
1133                            Content {
1134                                attributes: Some(attributes),
1135                                properties: None,
1136                            },
1137                        ),
1138                    };
1139                    ast.commands.push(command);
1140                }
1141                OverlayType::Entry => {
1142                    let entry = overlay.as_any().downcast_ref::<overlay::Entry>().unwrap();
1143                    let mut properties = IndexMap::new();
1144                    properties.insert(
1145                        "lang".to_string(),
1146                        NestedValue::Value(
1147                            entry.language().unwrap().to_639_1().unwrap().to_string(),
1148                        ),
1149                    );
1150                    let mut attributes = IndexMap::new();
1151                    for (attr_name, entries) in entry.attribute_entries.iter() {
1152                        match entries {
1153                            crate::state::entries::EntriesElement::Sai(said) => {
1154                                attributes.insert(
1155                                    attr_name.clone(),
1156                                    NestedValue::Value(said.to_string()),
1157                                );
1158                            }
1159                            crate::state::entries::EntriesElement::Object(entries) => {
1160                                attributes.insert(
1161                                    attr_name.clone(),
1162                                    NestedValue::Object(
1163                                        entries
1164                                            .iter()
1165                                            .map(|(k, v)| {
1166                                                (k.clone(), NestedValue::Value(v.clone()))
1167                                            })
1168                                            .collect(),
1169                                    ),
1170                                );
1171                            }
1172                        }
1173                    }
1174                    let command = Command {
1175                        kind: CommandType::Add,
1176                        object_kind: ObjectKind::Overlay(
1177                            OverlayType::Entry,
1178                            Content {
1179                                attributes: Some(attributes),
1180                                properties: Some(properties),
1181                            },
1182                        ),
1183                    };
1184                    ast.commands.push(command);
1185                }
1186                OverlayType::Cardinality => {
1187                    let cardinality = overlay
1188                        .as_any()
1189                        .downcast_ref::<overlay::Cardinality>()
1190                        .unwrap();
1191                    let mut attributes = IndexMap::new();
1192                    for (attr_name, cardinality) in cardinality.attribute_cardinality.iter() {
1193                        attributes
1194                            .insert(attr_name.clone(), NestedValue::Value(cardinality.clone()));
1195                    }
1196                    let command = Command {
1197                        kind: CommandType::Add,
1198                        object_kind: ObjectKind::Overlay(
1199                            OverlayType::Cardinality,
1200                            Content {
1201                                attributes: Some(attributes),
1202                                properties: None,
1203                            },
1204                        ),
1205                    };
1206                    ast.commands.push(command);
1207                }
1208                OverlayType::Unit => {
1209                    let unit_ov = overlay.as_any().downcast_ref::<overlay::Unit>().unwrap();
1210                    let mut properties = IndexMap::new();
1211                    let unit_system_val =
1212                        serde_json::to_value(unit_ov.measurement_system().unwrap()).unwrap();
1213                    properties.insert(
1214                        "unit_system".to_string(),
1215                        NestedValue::Value(unit_system_val.as_str().unwrap().to_string()),
1216                    );
1217                    let mut attributes = IndexMap::new();
1218                    for (attr_name, unit) in unit_ov.attribute_units.iter() {
1219                        let unit_val = serde_json::to_value(unit).unwrap();
1220                        attributes.insert(
1221                            attr_name.clone(),
1222                            NestedValue::Value(unit_val.as_str().unwrap().to_string()),
1223                        );
1224                    }
1225                    let command = Command {
1226                        kind: CommandType::Add,
1227                        object_kind: ObjectKind::Overlay(
1228                            OverlayType::Unit,
1229                            Content {
1230                                attributes: Some(attributes),
1231                                properties: Some(properties),
1232                            },
1233                        ),
1234                    };
1235                    ast.commands.push(command);
1236                }
1237                _ => {}
1238            }
1239        });
1240
1241        ast
1242    }
1243}
1244
1245/*
1246#[derive(Clone)]
1247struct AttributeLayoutValues {
1248    pub reference_sai: Option<String>,
1249    pub has_unit: bool,
1250}
1251
1252impl AttributeLayoutValues {
1253    pub fn new() -> Self {
1254        Self {
1255            reference_sai: None,
1256            has_unit: false,
1257        }
1258    }
1259
1260    pub fn add_reference_sai(&mut self, reference_sai: String) {
1261        self.reference_sai = Some(reference_sai);
1262    }
1263
1264    pub fn add_unit(&mut self) {
1265        self.has_unit = true;
1266    }
1267}
1268*/
1269
1270#[cfg(test)]
1271mod tests {
1272    use maplit::hashmap;
1273    use oca_ast::ast::{NestedAttrType, RefValue};
1274    use said::SelfAddressingIdentifier;
1275
1276    use super::*;
1277    use crate::state::{attribute::AttributeType, entries::EntriesElement};
1278
1279    #[test]
1280    fn build_oca_bundle() {
1281        let mut oca = OCABox::new();
1282        oca.add_classification("test".to_string());
1283        oca.add_meta(Language::Eng, "name".to_string(), "test name".to_string());
1284        oca.add_meta(
1285            Language::Eng,
1286            "description".to_string(),
1287            "test desc".to_string(),
1288        );
1289        let mut attr_remove = Attribute::new("removeme".to_string());
1290        attr_remove.set_attribute_type(NestedAttrType::Value(AttributeType::Text));
1291        oca.add_attribute(attr_remove);
1292
1293        let mut attr = Attribute::new("first_name".to_string());
1294        attr.set_attribute_type(NestedAttrType::Value(AttributeType::Text));
1295        oca.add_attribute(attr);
1296
1297        let mut attr2 = Attribute::new("gender".to_string());
1298        let entries = EntriesElement::Object(hashmap! {});
1299        attr2.set_entry(Language::Eng, entries);
1300        oca.remove_attribute(&"removeme".to_string());
1301
1302        let mut attr = Attribute::new("last_name".to_string());
1303        attr.set_attribute_type(NestedAttrType::Value(AttributeType::Text));
1304        oca.add_attribute(attr);
1305        // oca.add_attribute(Attribute::new("last_name".to_string()));
1306        let oca_bundle = oca.generate_bundle();
1307        /* let oca_bundle_encoded = oca_bundle.encode().unwrap();
1308        let oca_bundle_json = String::from_utf8(oca_bundle_encoded).unwrap();
1309        println!("{}", oca_bundle_json); */
1310        let said = oca_bundle.said;
1311        let oca_bundle = oca.generate_bundle();
1312        let oca_bundle_json = serde_json::to_string_pretty(&oca_bundle).unwrap();
1313        let said2 = oca_bundle.said;
1314        println!("{}", oca_bundle_json);
1315        assert_eq!(said, said2);
1316    }
1317
1318    #[test]
1319    fn load_oca_box_from_oca_bundle() {
1320        let mut oca = OCABox::new();
1321        oca.add_meta(Language::Eng, "name".to_string(), "test name".to_string());
1322        oca.add_meta(
1323            Language::Eng,
1324            "description".to_string(),
1325            "test desc".to_string(),
1326        );
1327        let mut attr = Attribute::new("first_name".to_string());
1328        attr.set_attribute_type(NestedAttrType::Value(AttributeType::Text));
1329        oca.add_attribute(attr);
1330
1331        let mut attr = Attribute::new("last_name".to_string());
1332        attr.set_attribute_type(NestedAttrType::Value(AttributeType::Text));
1333        attr.set_condition("string.len(${first_name}) > 0".to_string());
1334        oca.add_attribute(attr);
1335
1336        let mut attr = Attribute::new("ref".to_string());
1337        let said = SelfAddressingIdentifier::default();
1338        attr.set_attribute_type(NestedAttrType::Reference(RefValue::Said(said)));
1339        oca.add_attribute(attr);
1340
1341        let oca_bundle = oca.generate_bundle();
1342        let said = oca_bundle.said.clone();
1343
1344        let mut oca_box = OCABox::from(oca_bundle);
1345        let oca_bundle = oca_box.generate_bundle();
1346        let said2 = oca_bundle.said;
1347
1348        assert_eq!(said, said2);
1349    }
1350}
1351
1352/* struct CatAttributes {
1353    category_labels: HashMap<String, String>,
1354    categorized: IndexMap<String, IndexMap<String, AttributeLayoutValues>>,
1355    uncategorized: IndexMap<String, AttributeLayoutValues>,
1356    lang: String,
1357}
1358
1359impl CatAttributes {
1360    fn add_to_category(&mut self, categories: Vec<&str>, attribute: &Attribute) {
1361        let mut attribute_layout_values = AttributeLayoutValues::new();
1362        if let Some(sai) = &attribute.reference_sai {
1363            attribute_layout_values.add_reference_sai(sai.clone());
1364        }
1365        // if attribute.unit.is_some() {
1366        //     attribute_layout_values.add_unit();
1367        // }
1368        if categories.is_empty() {
1369            self.uncategorized
1370                .insert(attribute.name.clone(), attribute_layout_values);
1371            return;
1372        }
1373        let mut supercats: Vec<i32> = vec![];
1374        for (i, category) in categories.iter().enumerate() {
1375            let supercats_str: Vec<String> = supercats.iter().map(|c| c.to_string()).collect();
1376            let mut supercat = String::new();
1377            if !supercats_str.is_empty() {
1378                supercat = format!("-{}", supercats_str.join("-"))
1379            }
1380            let regex = regex::Regex::new(format!("^_cat{supercat}(-[0-9]*)_$").as_str()).unwrap();
1381            let mut acctual_cat_id = String::new();
1382            let mut category_exists = false;
1383            for (cat_id, cat_label) in self.category_labels.iter() {
1384                if cat_label == category && regex.is_match(cat_id) {
1385                    let cat_temp = cat_id.replace('_', "");
1386                    let mut temp = cat_temp.split('-').collect::<Vec<&str>>();
1387                    temp.remove(0);
1388                    supercats = temp.iter().map(|c| c.parse::<i32>().unwrap()).collect();
1389                    acctual_cat_id = cat_id.to_string();
1390                    category_exists = true;
1391                }
1392            }
1393
1394            if !category_exists {
1395                let mut count = 0;
1396                for cat in self.categorized.keys() {
1397                    if regex.is_match(cat.as_str()) {
1398                        count += 1;
1399                    }
1400                }
1401                acctual_cat_id = format!("_cat{}-{}_", supercat, count + 1);
1402                supercats.push(count + 1);
1403                self.category_labels
1404                    .insert(acctual_cat_id.clone(), category.to_string());
1405                self.categorized
1406                    .insert(acctual_cat_id.clone(), IndexMap::new());
1407            }
1408
1409            if i + 1 == categories.len() {
1410                self.categorized
1411                    .get_mut(acctual_cat_id.as_str())
1412                    .unwrap()
1413                    .insert(attribute.name.clone(), attribute_layout_values.clone());
1414            }
1415        }
1416    }
1417}
1418 */
1419
1420/*
1421#[cfg(test)]
1422mod tests {
1423    use super::*;
1424    use crate::state::{
1425        attribute::{AttributeType, Entries, Entry},
1426        encoding::Encoding,
1427        entry_codes::EntryCodes,
1428    };
1429    use maplit::hashmap;
1430
1431    #[test]
1432    fn build_oca_without_attributes() {
1433        let oca = OCABuilder::new(Encoding::Utf8)
1434            .add_classification("GICS:35102020".to_string())
1435            /*             .add_name(hashmap! {
1436                "En".to_string() => "Driving Licence".to_string(),
1437                "Pl".to_string() => "Prawo Jazdy".to_string(),
1438            })
1439            .add_description(hashmap! {
1440                "En".to_string() => "Driving Licence".to_string(),
1441                "Pl".to_string() => "Prawo Jazdy".to_string(),
1442            }) */
1443            .finalize();
1444
1445        // println!("{:#?}", serde_json::to_string(&oca).unwrap());
1446
1447        assert_eq!(oca.capture_base.attributes.len(), 0);
1448        assert_eq!(oca.capture_base.classification, "GICS:35102020");
1449    }
1450    /*
1451        #[test]
1452        fn build_oca_with_attributes() {
1453            let oca_builder = OCABuilder::new(Encoding::Utf8)
1454                .add_form_layout(
1455                    "
1456                    config:
1457                        css:
1458                            style: >-
1459                                form { background-color: white; }
1460                    elements:
1461                        - type: meta
1462                          config:
1463                              css:
1464                                  style: >-
1465                                      justify-content: space-between;
1466                          parts:
1467                              - name: language
1468                              - name: name
1469                              - name: description
1470                        - type: category
1471                          id: _cat-1_
1472                        - type: attribute
1473                          name: n1
1474                          parts:
1475                              - name: label
1476                              - name: input
1477                              - name: information
1478                        - type: attribute
1479                          name: n2
1480                          parts:
1481                              - name: label
1482                              - name: input
1483                              - name: information
1484                "
1485                    .to_string(),
1486                )
1487                .add_credential_layout(
1488                    "
1489    version: beta-1
1490    config:
1491      css:
1492        width: 200px
1493        height: 100px
1494        style: >-
1495          @import url('https://fonts.googleapis.com/css2?family=Nanum+Gothic:wght@700&display=swap');
1496    pages:
1497      - config:
1498          name: Page 0
1499        elements:
1500          - type: row
1501            config:
1502              css:
1503                style: >-
1504                  height: 32px;
1505            elements:
1506          - type: row
1507            elements:
1508              - type: col
1509                size: 4
1510                config:
1511                  css:
1512                    style: >-
1513                      padding-right: 0;
1514                elements:
1515                  - type: row
1516                    elements:
1517                      - type: col
1518                        size: 8
1519                        elements:
1520                          - type: row
1521                            elements:
1522                              - type: col
1523                                elements:
1524                                - type: attribute
1525                                  name: n1
1526    labels:
1527      passport:
1528        en: Passport
1529        fr: Passeport
1530                                       "
1531                    .to_string(),
1532                )
1533                .add_name(hashmap! {
1534                    "En".to_string() => "Driving Licence".to_string(),
1535                    "Pl".to_string() => "Prawo Jazdy".to_string(),
1536                })
1537                .add_description(hashmap! {
1538                    "En".to_string() => "DL desc".to_string(),
1539                    "Pl".to_string() => "PJ desc".to_string(),
1540                });
1541
1542            let attr1 = AttributeBuilder::new(String::from("n1"), AttributeType::Text)
1543                .set_flagged()
1544                .add_label(hashmap! {
1545                    "En".to_string() => "Name: ".to_string(),
1546                    "Pl".to_string() => "ImiÄ™: ".to_string(),
1547                })
1548                .add_entry_codes(EntryCodes::Array(vec![
1549                    "op1".to_string(),
1550                    "op2".to_string(),
1551                ]))
1552                .add_entries(Entries::Object(vec![
1553                    Entry::new(
1554                        "op1".to_string(),
1555                        hashmap! {
1556                            "En".to_string() => "Option 1".to_string(),
1557                            "Pl".to_string() => "Opcja 1".to_string(),
1558                        },
1559                    ),
1560                    Entry::new(
1561                        "op2".to_string(),
1562                        hashmap! {
1563                            "En".to_string() => "Option 2".to_string(),
1564                            "Pl".to_string() => "Opcja 2".to_string(),
1565                        },
1566                    ),
1567                ]))
1568                .add_information(hashmap! {
1569                    "En".to_string() => "info en".to_string(),
1570                    "Pl".to_string() => "info pl".to_string(),
1571                })
1572                .add_unit("SI".to_string(), "cm".to_string())
1573                .build();
1574
1575            let attr2 = AttributeBuilder::new(String::from("n2"), AttributeType::DateTime)
1576                .add_label(hashmap! {
1577                    "En".to_string() => "Date: ".to_string(),
1578                    "Pl".to_string() => "Data: ".to_string(),
1579                })
1580                .add_condition("${0} == 'op1'".to_string(), vec!["n1".to_string()])
1581                .add_encoding(Encoding::Iso8859_1)
1582                .add_format("DD/MM/YYYY".to_string())
1583                .build();
1584
1585            let attr3 = AttributeBuilder::new(String::from("n3"), AttributeType::Reference)
1586                .add_sai("sai".to_string())
1587                .add_label(hashmap! {
1588                    "En".to_string() => "Reference: ".to_string(),
1589                    "Pl".to_string() => "Referecja: ".to_string(),
1590                })
1591                .build();
1592
1593            let oca = oca_builder
1594                .add_attribute(attr1)
1595                .add_attribute(attr2)
1596                .add_attribute(attr3)
1597                .finalize();
1598
1599            // println!(
1600            //     "{}",
1601            //     serde_json::to_string_pretty(&serde_json::to_value(&oca).unwrap()).unwrap()
1602            // );
1603
1604            assert_eq!(
1605                oca.capture_base.attributes.get(&"n3".to_string()),
1606                Some(&"Reference:sai".to_string())
1607            );
1608            assert_eq!(oca.capture_base.attributes.len(), 3);
1609            assert_eq!(oca.capture_base.flagged_attributes.len(), 1);
1610        } */
1611}
1612*/