oca_bundle/
build.rs

1use crate::state::oca::overlay::cardinality::Cardinalitys;
2use crate::state::oca::overlay::character_encoding::CharacterEncodings;
3use crate::state::oca::overlay::conditional::Conditionals;
4use crate::state::oca::overlay::conformance::Conformances;
5use crate::state::oca::overlay::entry::Entries;
6use crate::state::oca::overlay::entry_code::EntryCodes;
7#[cfg(feature = "format_overlay")]
8use crate::state::oca::overlay::format::Formats;
9use crate::state::oca::overlay::information::Information;
10use crate::state::oca::overlay::label::Labels;
11use crate::state::oca::overlay::meta::Metas;
12use crate::state::oca::overlay::unit::{
13    AttributeUnit, ImperialUnit, MeasurementSystem, MeasurementUnit, MetricUnit, Unit,
14};
15use crate::state::oca::OCABundle;
16use crate::state::{
17    attribute::Attribute, encoding::Encoding, entries::EntriesElement,
18    entry_codes::EntryCodes as EntryCodesValue, oca::OCABox,
19};
20use indexmap::IndexMap;
21use oca_ast::ast;
22use std::collections::HashMap;
23use std::str::FromStr;
24
25#[derive(Debug)]
26pub struct OCABuild {
27    pub oca_bundle: OCABundle,
28    pub steps: Vec<OCABuildStep>,
29}
30
31#[derive(Debug)]
32pub struct OCABuildStep {
33    pub parent_said: Option<said::SelfAddressingIdentifier>,
34    pub command: ast::Command,
35    pub result: OCABundle,
36}
37
38#[derive(Debug, Clone, serde::Serialize)]
39pub struct FromASTError {
40    pub line_number: usize,
41    pub raw_line: String,
42    pub message: String,
43}
44
45#[derive(thiserror::Error, Debug, Clone, serde::Serialize)]
46#[serde(untagged)]
47pub enum Error {
48    #[error("Error at line {line_number} ({raw_line}): {message}")]
49    FromASTError {
50        #[serde(rename = "ln")]
51        line_number: usize,
52        #[serde(rename = "c")]
53        raw_line: String,
54        #[serde(rename = "e")]
55        message: String,
56    },
57}
58
59pub fn from_ast(
60    from_oca: Option<OCABundle>,
61    oca_ast: &ast::OCAAst,
62) -> Result<OCABuild, Vec<Error>> {
63    let mut errors = vec![];
64    let mut steps = vec![];
65    let mut parent_said: Option<said::SelfAddressingIdentifier> = match &from_oca {
66        Some(oca_bundle) => oca_bundle.said.clone(),
67        None => None,
68    };
69    let mut base: Option<OCABox> = from_oca.clone().map(OCABox::from);
70    let default_command_meta = ast::CommandMeta {
71        line_number: 0,
72        raw_line: "unknown".to_string(),
73    };
74    for (i, command) in oca_ast.commands.iter().enumerate() {
75        let command_index = match &from_oca {
76            Some(_) => i + 1,
77            None => i,
78        };
79        // todo pass the references
80        let command_meta = oca_ast
81            .commands_meta
82            .get(&command_index)
83            .unwrap_or(&default_command_meta);
84        match apply_command(base.clone(), command.clone()) {
85            Ok(oca_box) => {
86                let mut oca_box_mut = oca_box.clone();
87                let oca_bundle = oca_box_mut.generate_bundle();
88                if oca_bundle.said == parent_said {
89                    errors.push(Error::FromASTError {
90                        line_number: command_meta.line_number,
91                        raw_line: command_meta.raw_line.clone(),
92                        message: "Applying command failed".to_string(),
93                    });
94                } else {
95                    steps.push(OCABuildStep {
96                        parent_said: parent_said.clone(),
97                        command: command.clone(),
98                        result: oca_bundle.clone(),
99                    });
100                    parent_said.clone_from(&oca_bundle.said);
101                    base = Some(oca_box);
102                }
103            }
104            Err(mut err) => {
105                errors.extend(err.iter_mut().map(|e| Error::FromASTError {
106                    line_number: command_meta.line_number,
107                    raw_line: command_meta.raw_line.clone(),
108                    message: e.clone(),
109                }));
110            }
111        }
112    }
113    if errors.is_empty() {
114        Ok(OCABuild {
115            oca_bundle: base.unwrap().generate_bundle(),
116            steps,
117        })
118    } else {
119        Err(errors)
120    }
121}
122
123pub fn apply_command(base: Option<OCABox>, op: ast::Command) -> Result<OCABox, Vec<String>> {
124    let mut errors = vec![];
125    let mut oca: OCABox = match base {
126        Some(oca) => oca,
127        None => OCABox::new(),
128    };
129
130    match (op.kind, op.object_kind) {
131        (ast::CommandType::From, _) => {
132            errors.push(
133                "Unsupported FROM command, it should be resolved before applying commands"
134                    .to_string(),
135            );
136        }
137        (ast::CommandType::Add, ast::ObjectKind::CaptureBase(content)) => {
138            if let Some(ref attributes) = content.attributes {
139                for (attr_name, attr_type) in attributes {
140                    let mut attribute = Attribute::new(attr_name.clone());
141                    attribute.set_attribute_type(attr_type.clone());
142                    oca.add_attribute(attribute);
143                }
144            }
145            if let Some(ref properties) = content.properties {
146                // TODO handle other properties
147                for (prop_name, prop_value) in properties {
148                    if prop_name.eq("classification") {
149                        if let ast::NestedValue::Value(value) = prop_value {
150                            oca.add_classification(value.clone());
151                        }
152                    }
153                }
154            }
155        }
156        (ast::CommandType::Add, ast::ObjectKind::Overlay(overlay_type, content)) => {
157            match overlay_type {
158                ast::OverlayType::Meta => {
159                    if let Some(ref properties) = content.properties {
160                        let mut mut_properties = properties.clone();
161                        let lang = mut_properties.remove("lang");
162                        let mut lang_iso = None;
163                        if let Some(ast::NestedValue::Value(lang_str)) = lang {
164                            lang_iso = isolang::Language::from_639_1(lang_str.as_str());
165                        }
166
167                        for (prop_name, prop_value) in mut_properties {
168                            if let ast::NestedValue::Value(value) = prop_value {
169                                oca.add_meta(lang_iso.unwrap(), prop_name.clone(), value.clone());
170                            }
171                        }
172                    }
173                }
174                ast::OverlayType::Label => {
175                    let mut lang_iso = None;
176                    if let Some(ref properties) = content.properties {
177                        let mut mut_properties = properties.clone();
178                        let lang = mut_properties.remove("lang");
179                        if let Some(ast::NestedValue::Value(lang_str)) = lang {
180                            lang_iso = isolang::Language::from_639_1(lang_str.as_str());
181                        }
182                    }
183                    if let Some(ref attributes) = content.attributes {
184                        for (attr_name, attr_type_value) in attributes {
185                            let mut attribute = oca
186                                .attributes
187                                .get(attr_name)
188                                .ok_or_else(|| {
189                                    errors.push(format!("Undefined attribute: {attr_name}"));
190                                    errors.clone()
191                                })?
192                                .clone();
193                            if let ast::NestedValue::Value(attr_label) = attr_type_value {
194                                attribute.set_label(lang_iso.unwrap(), attr_label.clone());
195                            }
196                            oca.add_attribute(attribute);
197                        }
198                    }
199                }
200                ast::OverlayType::Information => {
201                    let mut lang_iso = None;
202                    if let Some(ref properties) = content.properties {
203                        let mut mut_properties = properties.clone();
204                        let lang = mut_properties.remove("lang");
205                        if let Some(ast::NestedValue::Value(lang_str)) = lang {
206                            lang_iso = isolang::Language::from_639_1(lang_str.as_str());
207                        }
208                    }
209                    if let Some(ref attributes) = content.attributes {
210                        for (attr_name, attr_type_value) in attributes {
211                            let mut attribute = oca
212                                .attributes
213                                .get(attr_name)
214                                .ok_or_else(|| {
215                                    errors.push(format!("Undefined attribute: {attr_name}"));
216                                    errors.clone()
217                                })?
218                                .clone();
219                            if let ast::NestedValue::Value(attr_info) = attr_type_value {
220                                attribute.set_information(lang_iso.unwrap(), attr_info.clone());
221                            }
222                            oca.add_attribute(attribute);
223                        }
224                    }
225                }
226                ast::OverlayType::CharacterEncoding => {
227                    if let Some(ref attributes) = content.attributes {
228                        for (attr_name, attr_type_value) in attributes {
229                            let mut attribute = oca
230                                .attributes
231                                .get(attr_name)
232                                .ok_or_else(|| {
233                                    errors.push(format!("Undefined attribute: {attr_name}"));
234                                    errors.clone()
235                                })?
236                                .clone();
237                            if let ast::NestedValue::Value(attr_encoding) = attr_type_value {
238                                attribute.set_encoding(Encoding::from_str(attr_encoding).map_err(
239                                    |_| {
240                                        errors.push(format!("Unknown encoding: {attr_encoding}"));
241                                        errors.clone()
242                                    },
243                                )?);
244                            }
245                            oca.add_attribute(attribute);
246                        }
247                    }
248                }
249                ast::OverlayType::Conformance => {
250                    if let Some(ref attributes) = content.attributes {
251                        for (attr_name, attr_type_value) in attributes {
252                            let mut attribute = oca
253                                .attributes
254                                .get(attr_name)
255                                .ok_or_else(|| {
256                                    errors.push(format!("Undefined attribute: {attr_name}"));
257                                    errors.clone()
258                                })?
259                                .clone();
260
261                            if let ast::NestedValue::Value(attr_conformance) = attr_type_value {
262                                attribute.set_conformance(attr_conformance.clone());
263                            }
264                            oca.add_attribute(attribute);
265                        }
266                    }
267                }
268                ast::OverlayType::Format => {
269                    #[cfg(feature = "format_overlay")]
270                    {
271                        if let Some(ref attributes) = content.attributes {
272                            for (attr_name, attr_type_value) in attributes {
273                                let mut attribute = oca
274                                    .attributes
275                                    .get(attr_name)
276                                    .ok_or_else(|| {
277                                        errors.push(format!("Undefined attribute: {attr_name}"));
278                                        errors.clone()
279                                    })?
280                                    .clone();
281                                if let ast::NestedValue::Value(attr_format) = attr_type_value {
282                                    attribute.set_format(attr_format.clone());
283                                }
284                                oca.add_attribute(attribute);
285                            }
286                        }
287                    }
288                }
289                ast::OverlayType::Unit => {
290                    let mut unit_system_op = None;
291                    if let Some(ref properties) = content.properties {
292                        let mut mut_properties = properties.clone();
293                        let unit_system_prop = mut_properties.remove("unit_system");
294                        if let Some(ast::NestedValue::Value(unit_system_str)) = unit_system_prop {
295                            unit_system_op = MeasurementSystem::from_str(&unit_system_str).ok();
296                        }
297                    }
298                    if let Some(unit_system) = unit_system_op {
299                        if let Some(ref attributes) = content.attributes {
300                            for (attr_name, attr_type_value) in attributes {
301                                let mut attribute = oca
302                                    .attributes
303                                    .get(attr_name)
304                                    .ok_or_else(|| {
305                                        errors.push(format!("Undefined attribute: {attr_name}"));
306                                        errors.clone()
307                                    })?
308                                    .clone();
309                                if let ast::NestedValue::Value(attr_unit) = attr_type_value {
310                                    let unit = match unit_system {
311                                        MeasurementSystem::Metric => Some(MeasurementUnit::Metric(
312                                            MetricUnit::from_str(attr_unit).unwrap_or_else(|_| {
313                                                panic!("{}", "Invalid metric unit: {attr_unit}")
314                                            }),
315                                        )),
316                                        MeasurementSystem::Imperial => {
317                                            Some(MeasurementUnit::Imperial(
318                                                ImperialUnit::from_str(attr_unit).unwrap_or_else(
319                                                    |_| {
320                                                        panic!(
321                                                            "{}",
322                                                            "Invalid imperial unit: {attr_unit}"
323                                                        )
324                                                    },
325                                                ),
326                                            ))
327                                        }
328                                    };
329                                    attribute.set_unit(AttributeUnit {
330                                        measurement_system: unit_system.clone(),
331                                        unit: unit.unwrap(),
332                                    });
333                                }
334                                oca.add_attribute(attribute);
335                            }
336                        }
337                    }
338                }
339                ast::OverlayType::Cardinality => {
340                    if let Some(ref attributes) = content.attributes {
341                        for (attr_name, attr_type_value) in attributes {
342                            let mut attribute = oca
343                                .attributes
344                                .get(attr_name)
345                                .ok_or_else(|| {
346                                    errors.push(format!("Undefined attribute: {attr_name}"));
347                                    errors.clone()
348                                })?
349                                .clone();
350                            if let ast::NestedValue::Value(attr_cardinality) = attr_type_value {
351                                attribute.set_cardinality(attr_cardinality.clone());
352                            }
353                            oca.add_attribute(attribute);
354                        }
355                    }
356                }
357                ast::OverlayType::Conditional => {
358                    if let Some(ref attributes) = content.attributes {
359                        for (attr_name, attr_type_value) in attributes {
360                            let mut attribute = oca
361                                .attributes
362                                .get(attr_name)
363                                .ok_or_else(|| {
364                                    errors.push(format!("Undefined attribute: {attr_name}"));
365                                    errors.clone()
366                                })?
367                                .clone();
368                            if let ast::NestedValue::Value(attr_condition) = attr_type_value {
369                                attribute.set_condition(attr_condition.clone());
370                            }
371                            oca.add_attribute(attribute);
372                        }
373                    }
374                }
375                ast::OverlayType::EntryCode => {
376                    if let Some(ref attributes) = content.attributes {
377                        for (attr_name, attr_type_value) in attributes {
378                            let mut attribute = oca
379                                .attributes
380                                .get(attr_name)
381                                .ok_or_else(|| {
382                                    errors.push(format!("Undefined attribute: {attr_name}"));
383                                    errors.clone()
384                                })?
385                                .clone();
386                            match attr_type_value {
387                                ast::NestedValue::Value(attr_entry_codes_sai) => {
388                                    attribute.set_entry_codes(EntryCodesValue::Sai(
389                                        attr_entry_codes_sai.clone(),
390                                    ));
391                                }
392                                ast::NestedValue::Array(attr_entry_codes) => {
393                                    let mut entry_codes: Vec<String> = vec![];
394                                    for attr_entry_code in attr_entry_codes {
395                                        if let ast::NestedValue::Value(entry_code) = attr_entry_code
396                                        {
397                                            entry_codes.push(entry_code.clone());
398                                        }
399                                    }
400                                    attribute.set_entry_codes(EntryCodesValue::Array(entry_codes));
401                                }
402                                ast::NestedValue::Object(attr_grouped_entry_codes) => {
403                                    let mut grouped_entry_codes = IndexMap::new();
404                                    for (group, attr_entry_codes) in attr_grouped_entry_codes {
405                                        if let ast::NestedValue::Array(entry_codes) = attr_entry_codes
406                                        {
407                                            let codes: Vec<String> = entry_codes
408                                                .iter()
409                                                .filter_map(|entry_code| {
410                                                    if let ast::NestedValue::Value(entry_code) =
411                                                        entry_code
412                                                    {
413                                                        Some(entry_code.clone())
414                                                    } else {
415                                                        None
416                                                    }
417                                                })
418                                                .collect();
419                                            grouped_entry_codes.insert(group.clone(), codes.clone());
420                                        }
421                                    }
422                                    attribute.set_entry_codes(EntryCodesValue::Object(grouped_entry_codes));
423                                }
424                                _ => (),
425                            }
426                            oca.add_attribute(attribute);
427                        }
428                    }
429                }
430                ast::OverlayType::Entry => {
431                    let mut lang_iso = None;
432                    if let Some(ref properties) = content.properties {
433                        let mut mut_properties = properties.clone();
434                        let lang = mut_properties.remove("lang");
435                        if let Some(ast::NestedValue::Value(lang_str)) = lang {
436                            lang_iso = isolang::Language::from_639_1(lang_str.as_str());
437                        }
438                    }
439                    if let Some(ref attributes) = content.attributes {
440                        for (attr_name, attr_type_value) in attributes {
441                            let mut attribute = oca
442                                .attributes
443                                .get(attr_name)
444                                .ok_or_else(|| {
445                                    errors.push(format!("Undefined attribute: {attr_name}"));
446                                    errors.clone()
447                                })?
448                                .clone();
449                            match attr_type_value {
450                                ast::NestedValue::Value(attr_entries) => {
451                                    attribute.set_entry(
452                                        lang_iso.unwrap(),
453                                        EntriesElement::Sai(attr_entries.clone()),
454                                    );
455                                }
456                                ast::NestedValue::Object(attr_entries) => {
457                                    let mut entries = HashMap::new();
458                                    for (attr_entry_key, attr_entry_value) in attr_entries {
459                                        if let ast::NestedValue::Value(entry_value) =
460                                            attr_entry_value
461                                        {
462                                            entries.insert(
463                                                attr_entry_key.clone(),
464                                                entry_value.clone(),
465                                            );
466                                        }
467                                    }
468                                    attribute.set_entry(
469                                        lang_iso.unwrap(),
470                                        EntriesElement::Object(entries),
471                                    );
472                                }
473                                _ => (),
474                            }
475                            oca.add_attribute(attribute);
476                        }
477                    }
478                }
479                _ => (),
480            }
481        }
482        (ast::CommandType::Add, ast::ObjectKind::OCABundle(_)) => todo!(),
483        (ast::CommandType::Remove, ast::ObjectKind::CaptureBase(content)) => {
484            if let Some(ref attributes) = content.attributes {
485                for (attr_name, _) in attributes {
486                    oca.remove_attribute(attr_name);
487                }
488            }
489            if let Some(ref properties) = content.properties {
490                for (prop_name, _) in properties {
491                    if prop_name.eq("classification") {
492                        oca.remove_classification()
493                    }
494                }
495            }
496        }
497        (ast::CommandType::Remove, ast::ObjectKind::OCABundle(_)) => todo!(),
498        (ast::CommandType::Remove, ast::ObjectKind::Overlay(_, _)) => todo!(),
499        (ast::CommandType::Modify, ast::ObjectKind::CaptureBase(_)) => todo!(),
500        (ast::CommandType::Modify, ast::ObjectKind::OCABundle(_)) => todo!(),
501        (ast::CommandType::Modify, ast::ObjectKind::Overlay(_, _)) => todo!(),
502    }
503
504    if errors.is_empty() {
505        Ok(oca)
506    } else {
507        Err(errors)
508    }
509}
510
511#[cfg(test)]
512mod tests {
513    use super::*;
514    use indexmap::IndexMap;
515    use oca_ast::ast::{AttributeType, CaptureContent};
516    use said::version::Encode;
517
518    #[test]
519    fn test_add_step() {
520        let mut commands = vec![];
521
522        let mut attributes = IndexMap::new();
523        attributes.insert(
524            "d".to_string(),
525            ast::NestedAttrType::Value(AttributeType::Text),
526        );
527        attributes.insert(
528            "i".to_string(),
529            ast::NestedAttrType::Value(AttributeType::Text),
530        );
531        attributes.insert(
532            "passed".to_string(),
533            ast::NestedAttrType::Value(AttributeType::Boolean),
534        );
535        commands.push(ast::Command {
536            kind: ast::CommandType::Add,
537            object_kind: ast::ObjectKind::CaptureBase(CaptureContent {
538                attributes: Some(attributes),
539                properties: None,
540                flagged_attributes: None,
541            }),
542        });
543
544        let mut properties = IndexMap::new();
545        properties.insert(
546            "lang".to_string(),
547            ast::NestedValue::Value("en".to_string()),
548        );
549        properties.insert(
550            "name".to_string(),
551            ast::NestedValue::Value("Entrance credential".to_string()),
552        );
553        properties.insert(
554            "description".to_string(),
555            ast::NestedValue::Value("Entrance credential".to_string()),
556        );
557        commands.push(ast::Command {
558            kind: ast::CommandType::Add,
559            object_kind: ast::ObjectKind::Overlay(
560                ast::OverlayType::Meta,
561                ast::Content {
562                    attributes: None,
563                    properties: Some(properties),
564                },
565            ),
566        });
567
568        let mut attributes = IndexMap::new();
569        attributes.insert(
570            "d".to_string(),
571            ast::NestedValue::Value("Schema digest".to_string()),
572        );
573        attributes.insert(
574            "i".to_string(),
575            ast::NestedValue::Value("Credential Issuee".to_string()),
576        );
577        attributes.insert(
578            "passed".to_string(),
579            ast::NestedValue::Value("Passed".to_string()),
580        );
581        let mut properties = IndexMap::new();
582        properties.insert(
583            "lang".to_string(),
584            ast::NestedValue::Value("en".to_string()),
585        );
586        commands.push(ast::Command {
587            kind: ast::CommandType::Add,
588            object_kind: ast::ObjectKind::Overlay(
589                ast::OverlayType::Label,
590                ast::Content {
591                    attributes: Some(attributes),
592                    properties: Some(properties),
593                },
594            ),
595        });
596
597        let mut attributes = IndexMap::new();
598        attributes.insert(
599            "d".to_string(),
600            ast::NestedValue::Value("Schema digest".to_string()),
601        );
602        attributes.insert(
603            "i".to_string(),
604            ast::NestedValue::Value("Credential Issuee".to_string()),
605        );
606        attributes.insert(
607            "passed".to_string(),
608            ast::NestedValue::Value("Enables or disables passing".to_string()),
609        );
610        let mut properties = IndexMap::new();
611        properties.insert(
612            "lang".to_string(),
613            ast::NestedValue::Value("en".to_string()),
614        );
615        commands.push(ast::Command {
616            kind: ast::CommandType::Add,
617            object_kind: ast::ObjectKind::Overlay(
618                ast::OverlayType::Information,
619                ast::Content {
620                    attributes: Some(attributes),
621                    properties: Some(properties),
622                },
623            ),
624        });
625
626        let mut attributes = IndexMap::new();
627        attributes.insert(
628            "d".to_string(),
629            ast::NestedValue::Value("utf-8".to_string()),
630        );
631        attributes.insert(
632            "i".to_string(),
633            ast::NestedValue::Value("utf-8".to_string()),
634        );
635        attributes.insert(
636            "passed".to_string(),
637            ast::NestedValue::Value("utf-8".to_string()),
638        );
639        commands.push(ast::Command {
640            kind: ast::CommandType::Add,
641            object_kind: ast::ObjectKind::Overlay(
642                ast::OverlayType::CharacterEncoding,
643                ast::Content {
644                    attributes: Some(attributes),
645                    properties: None,
646                },
647            ),
648        });
649
650        let mut attributes = IndexMap::new();
651        attributes.insert("d".to_string(), ast::NestedValue::Value("M".to_string()));
652        attributes.insert("i".to_string(), ast::NestedValue::Value("M".to_string()));
653        attributes.insert(
654            "passed".to_string(),
655            ast::NestedValue::Value("M".to_string()),
656        );
657        commands.push(ast::Command {
658            kind: ast::CommandType::Add,
659            object_kind: ast::ObjectKind::Overlay(
660                ast::OverlayType::Conformance,
661                ast::Content {
662                    attributes: Some(attributes),
663                    properties: None,
664                },
665            ),
666        });
667
668        // todo test if references with name are working
669        let mut base: Option<OCABox> = None;
670        for command in commands {
671            match apply_command(base.clone(), command.clone()) {
672                Ok(oca) => {
673                    base = Some(oca);
674                }
675                Err(errors) => {
676                    println!("{:?}", errors);
677                }
678            }
679            // let mut oca = apply_command(base, command);
680            // println!("{}", serde_json::to_string_pretty(&oca.generate_bundle()).unwrap());
681            // base = Some(oca);
682        }
683    }
684
685    #[test]
686    fn build_from_ast() {
687        let mut commands = vec![];
688
689        let mut attributes = IndexMap::new();
690        attributes.insert(
691            "d".to_string(),
692            ast::NestedAttrType::Value(AttributeType::Text),
693        );
694        attributes.insert(
695            "i".to_string(),
696            ast::NestedAttrType::Value(AttributeType::Text),
697        );
698        attributes.insert(
699            "list".to_string(),
700            ast::NestedAttrType::Value(AttributeType::Text),
701        );
702        attributes.insert(
703            "passed".to_string(),
704            ast::NestedAttrType::Value(AttributeType::Boolean),
705        );
706
707        let flagged_attributes = vec!["d".to_string(), "i".to_string()];
708        commands.push(ast::Command {
709            kind: ast::CommandType::Add,
710            object_kind: ast::ObjectKind::CaptureBase(ast::CaptureContent {
711                attributes: Some(attributes),
712                properties: None,
713                flagged_attributes: Some(flagged_attributes.clone()),
714            }),
715        });
716
717        let mut properties = IndexMap::new();
718        properties.insert(
719            "lang".to_string(),
720            ast::NestedValue::Value("en".to_string()),
721        );
722        properties.insert(
723            "name".to_string(),
724            ast::NestedValue::Value("Entrance credential".to_string()),
725        );
726        properties.insert(
727            "description".to_string(),
728            ast::NestedValue::Value("Entrance credential".to_string()),
729        );
730        commands.push(ast::Command {
731            kind: ast::CommandType::Add,
732            object_kind: ast::ObjectKind::Overlay(
733                ast::OverlayType::Meta,
734                ast::Content {
735                    attributes: None,
736                    properties: Some(properties),
737                },
738            ),
739        });
740
741        let mut attributes = IndexMap::new();
742        attributes.insert(
743            "d".to_string(),
744            ast::NestedValue::Value("Schema digest".to_string()),
745        );
746        attributes.insert(
747            "i".to_string(),
748            ast::NestedValue::Value("Credential Issuee".to_string()),
749        );
750        attributes.insert(
751            "passed".to_string(),
752            ast::NestedValue::Value("Passed".to_string()),
753        );
754        let mut properties = IndexMap::new();
755        properties.insert(
756            "lang".to_string(),
757            ast::NestedValue::Value("en".to_string()),
758        );
759        commands.push(ast::Command {
760            kind: ast::CommandType::Add,
761            object_kind: ast::ObjectKind::Overlay(
762                ast::OverlayType::Label,
763                ast::Content {
764                    attributes: Some(attributes),
765                    properties: Some(properties),
766                },
767            ),
768        });
769
770        let mut attributes = IndexMap::new();
771        attributes.insert(
772            "d".to_string(),
773            ast::NestedValue::Value("Schema digest".to_string()),
774        );
775        attributes.insert(
776            "i".to_string(),
777            ast::NestedValue::Value("Credential Issuee".to_string()),
778        );
779        attributes.insert(
780            "passed".to_string(),
781            ast::NestedValue::Value("Enables or disables passing".to_string()),
782        );
783        let mut properties = IndexMap::new();
784        properties.insert(
785            "lang".to_string(),
786            ast::NestedValue::Value("en".to_string()),
787        );
788        commands.push(ast::Command {
789            kind: ast::CommandType::Add,
790            object_kind: ast::ObjectKind::Overlay(
791                ast::OverlayType::Information,
792                ast::Content {
793                    attributes: Some(attributes),
794                    properties: Some(properties),
795                },
796            ),
797        });
798
799        let mut attributes = IndexMap::new();
800        attributes.insert(
801            "d".to_string(),
802            ast::NestedValue::Value("utf-8".to_string()),
803        );
804        attributes.insert(
805            "i".to_string(),
806            ast::NestedValue::Value("utf-8".to_string()),
807        );
808        attributes.insert(
809            "passed".to_string(),
810            ast::NestedValue::Value("utf-8".to_string()),
811        );
812        commands.push(ast::Command {
813            kind: ast::CommandType::Add,
814            object_kind: ast::ObjectKind::Overlay(
815                ast::OverlayType::CharacterEncoding,
816                ast::Content {
817                    attributes: Some(attributes),
818                    properties: None,
819                },
820            ),
821        });
822
823        let mut attributes = IndexMap::new();
824        attributes.insert("d".to_string(), ast::NestedValue::Value("M".to_string()));
825        attributes.insert("i".to_string(), ast::NestedValue::Value("M".to_string()));
826        attributes.insert(
827            "passed".to_string(),
828            ast::NestedValue::Value("M".to_string()),
829        );
830        commands.push(ast::Command {
831            kind: ast::CommandType::Add,
832            object_kind: ast::ObjectKind::Overlay(
833                ast::OverlayType::Conformance,
834                ast::Content {
835                    attributes: Some(attributes),
836                    properties: None,
837                },
838            ),
839        });
840
841        let mut attributes = IndexMap::new();
842        let mut grouped_elements = IndexMap::new();
843        grouped_elements.insert("g1".to_string(), ast::NestedValue::Array(
844            vec![ast::NestedValue::Value("el1".to_string())]
845        ));
846        grouped_elements.insert("g2".to_string(), ast::NestedValue::Array(
847            vec![ast::NestedValue::Value("el2".to_string()), ast::NestedValue::Value("el3".to_string())]
848        ));
849        attributes.insert("list".to_string(), oca_ast::ast::NestedValue::Object(grouped_elements));
850        commands.push(ast::Command {
851            kind: ast::CommandType::Add,
852            object_kind: ast::ObjectKind::Overlay(
853                ast::OverlayType::EntryCode,
854                ast::Content {
855                    attributes: Some(attributes),
856                    properties: None,
857                },
858            ),
859        });
860
861        let oca_ast = ast::OCAAst {
862            version: "1.0".to_string(),
863            commands,
864            commands_meta: IndexMap::new(),
865            meta: HashMap::new(),
866        };
867
868        let build_result = from_ast(None, &oca_ast);
869        match build_result {
870            Ok(oca_build) => {
871                let oca_bundle_encoded = oca_build.oca_bundle.encode().unwrap();
872                let oca_bundle_json = String::from_utf8(oca_bundle_encoded).unwrap();
873                println!("{}", oca_bundle_json);
874            }
875            Err(e) => {
876                println!("{:?}", e);
877            }
878        }
879    }
880}