oca_bundle_semantics/
build.rs

1use crate::state::oca::overlay::attribute_framing::{FramingScope, Framings};
2use crate::state::oca::overlay::cardinality::Cardinalitys;
3use crate::state::oca::overlay::character_encoding::CharacterEncodings;
4use crate::state::oca::overlay::conditional::Conditionals;
5use crate::state::oca::overlay::conformance::Conformances;
6use crate::state::oca::overlay::entry::Entries;
7use crate::state::oca::overlay::entry_code::EntryCodes;
8#[cfg(feature = "format_overlay")]
9use crate::state::oca::overlay::format::Formats;
10use crate::state::oca::overlay::information::Information;
11use crate::state::oca::overlay::label::Labels;
12use crate::state::oca::overlay::link::Links;
13use crate::state::oca::overlay::meta::Metas;
14use crate::state::oca::overlay::unit::Units;
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_semantics::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                    if let Some(ref attributes) = content.attributes {
291                        for (attr_name, attr_type_value) in attributes {
292                            let mut attribute = oca
293                                .attributes
294                                .get(attr_name)
295                                .ok_or_else(|| {
296                                    errors.push(format!("Undefined attribute: {attr_name}"));
297                                    errors.clone()
298                                })?
299                                .clone();
300                            if let ast::NestedValue::Value(attr_unit) = attr_type_value {
301                                attribute.set_unit(attr_unit.clone());
302                            }
303                            oca.add_attribute(attribute);
304                        }
305                    }
306                }
307                ast::OverlayType::Cardinality(_) => {
308                    if let Some(ref attributes) = content.attributes {
309                        for (attr_name, attr_type_value) in attributes {
310                            let mut attribute = oca
311                                .attributes
312                                .get(attr_name)
313                                .ok_or_else(|| {
314                                    errors.push(format!("Undefined attribute: {attr_name}"));
315                                    errors.clone()
316                                })?
317                                .clone();
318                            if let ast::NestedValue::Value(attr_cardinality) = attr_type_value {
319                                attribute.set_cardinality(attr_cardinality.clone());
320                            }
321                            oca.add_attribute(attribute);
322                        }
323                    }
324                }
325                ast::OverlayType::Conditional(_) => {
326                    if let Some(ref attributes) = content.attributes {
327                        for (attr_name, attr_type_value) in attributes {
328                            let mut attribute = oca
329                                .attributes
330                                .get(attr_name)
331                                .ok_or_else(|| {
332                                    errors.push(format!("Undefined attribute: {attr_name}"));
333                                    errors.clone()
334                                })?
335                                .clone();
336                            if let ast::NestedValue::Value(attr_condition) = attr_type_value {
337                                attribute.set_condition(attr_condition.clone());
338                            }
339                            oca.add_attribute(attribute);
340                        }
341                    }
342                }
343                ast::OverlayType::EntryCode(_) => {
344                    if let Some(ref attributes) = content.attributes {
345                        for (attr_name, attr_type_value) in attributes {
346                            let mut attribute = oca
347                                .attributes
348                                .get(attr_name)
349                                .ok_or_else(|| {
350                                    errors.push(format!("Undefined attribute: {attr_name}"));
351                                    errors.clone()
352                                })?
353                                .clone();
354                            match attr_type_value {
355                                ast::NestedValue::Value(attr_entry_codes_sai) => {
356                                    attribute.set_entry_codes(EntryCodesValue::Sai(
357                                        attr_entry_codes_sai.clone(),
358                                    ));
359                                }
360                                ast::NestedValue::Array(attr_entry_codes) => {
361                                    let mut entry_codes: Vec<String> = vec![];
362                                    for attr_entry_code in attr_entry_codes {
363                                        if let ast::NestedValue::Value(entry_code) = attr_entry_code
364                                        {
365                                            entry_codes.push(entry_code.clone());
366                                        }
367                                    }
368                                    attribute.set_entry_codes(EntryCodesValue::Array(entry_codes));
369                                }
370                                ast::NestedValue::Object(attr_grouped_entry_codes) => {
371                                    let mut grouped_entry_codes = IndexMap::new();
372                                    for (group, attr_entry_codes) in attr_grouped_entry_codes {
373                                        if let ast::NestedValue::Array(entry_codes) =
374                                            attr_entry_codes
375                                        {
376                                            let codes: Vec<String> = entry_codes
377                                                .iter()
378                                                .filter_map(|entry_code| {
379                                                    if let ast::NestedValue::Value(entry_code) =
380                                                        entry_code
381                                                    {
382                                                        Some(entry_code.clone())
383                                                    } else {
384                                                        None
385                                                    }
386                                                })
387                                                .collect();
388                                            grouped_entry_codes
389                                                .insert(group.clone(), codes.clone());
390                                        }
391                                    }
392                                    attribute.set_entry_codes(EntryCodesValue::Object(
393                                        grouped_entry_codes,
394                                    ));
395                                }
396                                _ => (),
397                            }
398                            oca.add_attribute(attribute);
399                        }
400                    }
401                }
402                ast::OverlayType::Entry(_) => {
403                    let mut lang_iso = None;
404                    if let Some(ref properties) = content.properties {
405                        let mut mut_properties = properties.clone();
406                        let lang = mut_properties.remove("lang");
407                        if let Some(ast::NestedValue::Value(lang_str)) = lang {
408                            lang_iso = isolang::Language::from_639_1(lang_str.as_str());
409                        }
410                    }
411                    if let Some(ref attributes) = content.attributes {
412                        for (attr_name, attr_type_value) in attributes {
413                            let mut attribute = oca
414                                .attributes
415                                .get(attr_name)
416                                .ok_or_else(|| {
417                                    errors.push(format!("Undefined attribute: {attr_name}"));
418                                    errors.clone()
419                                })?
420                                .clone();
421                            match attr_type_value {
422                                ast::NestedValue::Value(attr_entries) => {
423                                    attribute.set_entry(
424                                        lang_iso.unwrap(),
425                                        EntriesElement::Sai(attr_entries.clone()),
426                                    );
427                                }
428                                ast::NestedValue::Object(attr_entries) => {
429                                    let mut entries = HashMap::new();
430                                    for (attr_entry_key, attr_entry_value) in attr_entries {
431                                        if let ast::NestedValue::Value(entry_value) =
432                                            attr_entry_value
433                                        {
434                                            entries.insert(
435                                                attr_entry_key.clone(),
436                                                entry_value.clone(),
437                                            );
438                                        }
439                                    }
440                                    attribute.set_entry(
441                                        lang_iso.unwrap(),
442                                        EntriesElement::Object(entries),
443                                    );
444                                }
445                                _ => (),
446                            }
447                            oca.add_attribute(attribute);
448                        }
449                    }
450                }
451                ast::OverlayType::Link(_) => {
452                    let mut target_bundle = None;
453                    if let Some(ref properties) = content.properties {
454                        if let Some(ast::NestedValue::Reference(ast::RefValue::Said(target_said))) =
455                            properties.get("target")
456                        {
457                            target_bundle = Some(target_said.to_string());
458                        }
459                    }
460                    if target_bundle.is_none() {
461                        errors.push("Undefined target bundle".to_string());
462                    }
463
464                    if let Some(ref attributes) = content.attributes {
465                        for (attr_name, attr_type_value) in attributes {
466                            let mut attribute = oca
467                                .attributes
468                                .get(attr_name)
469                                .ok_or_else(|| {
470                                    errors.push(format!("Undefined attribute: {attr_name}"));
471                                    errors.clone()
472                                })?
473                                .clone();
474                            if let ast::NestedValue::Value(linked_attr) = attr_type_value {
475                                attribute
476                                    .set_link(target_bundle.clone().unwrap(), linked_attr.clone());
477                            }
478                            oca.add_attribute(attribute);
479                        }
480                    }
481                }
482                ast::OverlayType::AttributeFraming(_) => {
483                    let mut frame_id = None;
484                    let mut frame_meta = HashMap::new();
485                    if let Some(ref properties) = content.properties {
486                        for prop in properties {
487                            if let (prop_name, ast::NestedValue::Value(prop_value)) = prop {
488                                if prop_name.eq("id") {
489                                    frame_id = Some(prop_value.clone());
490                                } else {
491                                    frame_meta
492                                        .insert(format!("frame_{}", prop_name), prop_value.clone());
493                                }
494                            }
495                        }
496                    }
497                    if frame_id.is_none() {
498                        errors.push("Undefined frame id".to_string());
499                    }
500
501                    if let Some(ref attributes) = content.attributes {
502                        for (attr_name, attr_framing_value) in attributes {
503                            let mut attribute = oca
504                                .attributes
505                                .get(attr_name)
506                                .ok_or_else(|| {
507                                    errors.push(format!("Undefined attribute: {attr_name}"));
508                                    errors.clone()
509                                })?
510                                .clone();
511                            if let ast::NestedValue::Object(attr_framing) = attr_framing_value {
512                                let mut framing = HashMap::new();
513                                for (framing_key, framing_value) in attr_framing {
514                                    if let ast::NestedValue::Object(framing_value) = framing_value {
515                                        if let Some(ast::NestedValue::Value(predicate_id)) =
516                                            framing_value.get("predicate_id")
517                                        {
518                                            if let Some(ast::NestedValue::Value(
519                                                framing_justification,
520                                            )) = framing_value.get("framing_justification")
521                                            {
522                                                let framing_scope = FramingScope {
523                                                    predicate_id: predicate_id.to_string(),
524                                                    framing_justification: framing_justification
525                                                        .to_string(),
526                                                    frame_meta: frame_meta.clone(),
527                                                };
528                                                framing.insert(framing_key.clone(), framing_scope);
529                                            }
530                                        }
531                                    }
532                                }
533                                attribute.set_framing(frame_id.clone().unwrap(), framing.clone());
534                            }
535                            oca.add_attribute(attribute);
536                        }
537                    }
538                }
539                _ => (),
540            }
541        }
542        (ast::CommandType::Add, ast::ObjectKind::OCABundle(_)) => todo!(),
543        (ast::CommandType::Remove, ast::ObjectKind::CaptureBase(content)) => {
544            if let Some(ref attributes) = content.attributes {
545                for (attr_name, _) in attributes {
546                    oca.remove_attribute(attr_name);
547                }
548            }
549            if let Some(ref properties) = content.properties {
550                for (prop_name, _) in properties {
551                    if prop_name.eq("classification") {
552                        oca.remove_classification()
553                    }
554                }
555            }
556        }
557        (ast::CommandType::Remove, ast::ObjectKind::OCABundle(_)) => todo!(),
558        (ast::CommandType::Remove, ast::ObjectKind::Overlay(_, _)) => todo!(),
559        (ast::CommandType::Modify, ast::ObjectKind::CaptureBase(_)) => todo!(),
560        (ast::CommandType::Modify, ast::ObjectKind::OCABundle(_)) => todo!(),
561        (ast::CommandType::Modify, ast::ObjectKind::Overlay(_, _)) => todo!(),
562    }
563
564    if errors.is_empty() {
565        Ok(oca)
566    } else {
567        Err(errors)
568    }
569}
570
571#[cfg(test)]
572mod tests {
573    use super::*;
574    use indexmap::IndexMap;
575    use oca_ast_semantics::ast::{AttributeType, CaptureContent};
576    use said::{derivation::HashFunctionCode, sad::SerializationFormats, version::Encode};
577
578    #[test]
579    fn test_add_step() {
580        let mut commands = vec![];
581
582        let mut attributes = IndexMap::new();
583        attributes.insert(
584            "d".to_string(),
585            ast::NestedAttrType::Value(AttributeType::Text),
586        );
587        attributes.insert(
588            "i".to_string(),
589            ast::NestedAttrType::Value(AttributeType::Text),
590        );
591        attributes.insert(
592            "passed".to_string(),
593            ast::NestedAttrType::Value(AttributeType::Boolean),
594        );
595        commands.push(ast::Command {
596            kind: ast::CommandType::Add,
597            object_kind: ast::ObjectKind::CaptureBase(CaptureContent {
598                attributes: Some(attributes),
599                properties: None,
600                flagged_attributes: None,
601            }),
602        });
603
604        let mut properties = IndexMap::new();
605        properties.insert(
606            "lang".to_string(),
607            ast::NestedValue::Value("en".to_string()),
608        );
609        properties.insert(
610            "name".to_string(),
611            ast::NestedValue::Value("Entrance credential".to_string()),
612        );
613        properties.insert(
614            "description".to_string(),
615            ast::NestedValue::Value("Entrance credential".to_string()),
616        );
617        let overlay_version = "1.1".to_string();
618        commands.push(ast::Command {
619            kind: ast::CommandType::Add,
620            object_kind: ast::ObjectKind::Overlay(
621                ast::OverlayType::Meta(overlay_version.clone()),
622                ast::Content {
623                    attributes: None,
624                    properties: Some(properties),
625                },
626            ),
627        });
628
629        let mut attributes = IndexMap::new();
630        attributes.insert(
631            "d".to_string(),
632            ast::NestedValue::Value("Schema digest".to_string()),
633        );
634        attributes.insert(
635            "i".to_string(),
636            ast::NestedValue::Value("Credential Issuee".to_string()),
637        );
638        attributes.insert(
639            "passed".to_string(),
640            ast::NestedValue::Value("Passed".to_string()),
641        );
642        let mut properties = IndexMap::new();
643        properties.insert(
644            "lang".to_string(),
645            ast::NestedValue::Value("en".to_string()),
646        );
647        commands.push(ast::Command {
648            kind: ast::CommandType::Add,
649            object_kind: ast::ObjectKind::Overlay(
650                ast::OverlayType::Label(overlay_version.clone()),
651                ast::Content {
652                    attributes: Some(attributes),
653                    properties: Some(properties),
654                },
655            ),
656        });
657
658        let mut attributes = IndexMap::new();
659        attributes.insert(
660            "d".to_string(),
661            ast::NestedValue::Value("Schema digest".to_string()),
662        );
663        attributes.insert(
664            "i".to_string(),
665            ast::NestedValue::Value("Credential Issuee".to_string()),
666        );
667        attributes.insert(
668            "passed".to_string(),
669            ast::NestedValue::Value("Enables or disables passing".to_string()),
670        );
671        let mut properties = IndexMap::new();
672        properties.insert(
673            "lang".to_string(),
674            ast::NestedValue::Value("en".to_string()),
675        );
676        commands.push(ast::Command {
677            kind: ast::CommandType::Add,
678            object_kind: ast::ObjectKind::Overlay(
679                ast::OverlayType::Information(overlay_version.clone()),
680                ast::Content {
681                    attributes: Some(attributes),
682                    properties: Some(properties),
683                },
684            ),
685        });
686
687        let mut attributes = IndexMap::new();
688        attributes.insert(
689            "d".to_string(),
690            ast::NestedValue::Value("utf-8".to_string()),
691        );
692        attributes.insert(
693            "i".to_string(),
694            ast::NestedValue::Value("utf-8".to_string()),
695        );
696        attributes.insert(
697            "passed".to_string(),
698            ast::NestedValue::Value("utf-8".to_string()),
699        );
700        commands.push(ast::Command {
701            kind: ast::CommandType::Add,
702            object_kind: ast::ObjectKind::Overlay(
703                ast::OverlayType::CharacterEncoding(overlay_version.clone()),
704                ast::Content {
705                    attributes: Some(attributes),
706                    properties: None,
707                },
708            ),
709        });
710
711        let mut attributes = IndexMap::new();
712        attributes.insert("d".to_string(), ast::NestedValue::Value("M".to_string()));
713        attributes.insert("i".to_string(), ast::NestedValue::Value("M".to_string()));
714        attributes.insert(
715            "passed".to_string(),
716            ast::NestedValue::Value("M".to_string()),
717        );
718        commands.push(ast::Command {
719            kind: ast::CommandType::Add,
720            object_kind: ast::ObjectKind::Overlay(
721                ast::OverlayType::Conformance(overlay_version.clone()),
722                ast::Content {
723                    attributes: Some(attributes),
724                    properties: None,
725                },
726            ),
727        });
728
729        // todo test if references with name are working
730        let mut base: Option<OCABox> = None;
731        for command in commands {
732            match apply_command(base.clone(), command.clone()) {
733                Ok(oca) => {
734                    base = Some(oca);
735                }
736                Err(errors) => {
737                    println!("{:?}", errors);
738                }
739            }
740            // let mut oca = apply_command(base, command);
741            // println!("{}", serde_json::to_string_pretty(&oca.generate_bundle()).unwrap());
742            // base = Some(oca);
743        }
744    }
745
746    #[test]
747    fn build_from_ast() {
748        let mut commands = vec![];
749
750        let mut attributes = IndexMap::new();
751        attributes.insert(
752            "d".to_string(),
753            ast::NestedAttrType::Value(AttributeType::Text),
754        );
755        attributes.insert(
756            "i".to_string(),
757            ast::NestedAttrType::Value(AttributeType::Text),
758        );
759        attributes.insert(
760            "list".to_string(),
761            ast::NestedAttrType::Value(AttributeType::Text),
762        );
763        attributes.insert(
764            "passed".to_string(),
765            ast::NestedAttrType::Value(AttributeType::Boolean),
766        );
767
768        let flagged_attributes = vec!["d".to_string(), "i".to_string()];
769        commands.push(ast::Command {
770            kind: ast::CommandType::Add,
771            object_kind: ast::ObjectKind::CaptureBase(ast::CaptureContent {
772                attributes: Some(attributes),
773                properties: None,
774                flagged_attributes: Some(flagged_attributes.clone()),
775            }),
776        });
777
778        let mut properties = IndexMap::new();
779        properties.insert(
780            "lang".to_string(),
781            ast::NestedValue::Value("en".to_string()),
782        );
783        properties.insert(
784            "name".to_string(),
785            ast::NestedValue::Value("Entrance credential".to_string()),
786        );
787        properties.insert(
788            "description".to_string(),
789            ast::NestedValue::Value("Entrance credential".to_string()),
790        );
791        let overlay_version = "1.1".to_string();
792        commands.push(ast::Command {
793            kind: ast::CommandType::Add,
794            object_kind: ast::ObjectKind::Overlay(
795                ast::OverlayType::Meta(overlay_version.clone()),
796                ast::Content {
797                    attributes: None,
798                    properties: Some(properties),
799                },
800            ),
801        });
802
803        let mut attributes = IndexMap::new();
804        attributes.insert(
805            "d".to_string(),
806            ast::NestedValue::Value("Schema digest".to_string()),
807        );
808        attributes.insert(
809            "i".to_string(),
810            ast::NestedValue::Value("Credential Issuee".to_string()),
811        );
812        attributes.insert(
813            "passed".to_string(),
814            ast::NestedValue::Value("Passed".to_string()),
815        );
816        let mut properties = IndexMap::new();
817        properties.insert(
818            "lang".to_string(),
819            ast::NestedValue::Value("en".to_string()),
820        );
821        commands.push(ast::Command {
822            kind: ast::CommandType::Add,
823            object_kind: ast::ObjectKind::Overlay(
824                ast::OverlayType::Label(overlay_version.clone()),
825                ast::Content {
826                    attributes: Some(attributes),
827                    properties: Some(properties),
828                },
829            ),
830        });
831
832        let mut attributes = IndexMap::new();
833        attributes.insert(
834            "d".to_string(),
835            ast::NestedValue::Value("Schema digest".to_string()),
836        );
837        attributes.insert(
838            "i".to_string(),
839            ast::NestedValue::Value("Credential Issuee".to_string()),
840        );
841        attributes.insert(
842            "passed".to_string(),
843            ast::NestedValue::Value("Enables or disables passing".to_string()),
844        );
845        let mut properties = IndexMap::new();
846        properties.insert(
847            "lang".to_string(),
848            ast::NestedValue::Value("en".to_string()),
849        );
850        commands.push(ast::Command {
851            kind: ast::CommandType::Add,
852            object_kind: ast::ObjectKind::Overlay(
853                ast::OverlayType::Information(overlay_version.clone()),
854                ast::Content {
855                    attributes: Some(attributes),
856                    properties: Some(properties),
857                },
858            ),
859        });
860
861        let mut attributes = IndexMap::new();
862        attributes.insert(
863            "d".to_string(),
864            ast::NestedValue::Value("utf-8".to_string()),
865        );
866        attributes.insert(
867            "i".to_string(),
868            ast::NestedValue::Value("utf-8".to_string()),
869        );
870        attributes.insert(
871            "passed".to_string(),
872            ast::NestedValue::Value("utf-8".to_string()),
873        );
874        commands.push(ast::Command {
875            kind: ast::CommandType::Add,
876            object_kind: ast::ObjectKind::Overlay(
877                ast::OverlayType::CharacterEncoding(overlay_version.clone()),
878                ast::Content {
879                    attributes: Some(attributes),
880                    properties: None,
881                },
882            ),
883        });
884
885        let mut attributes = IndexMap::new();
886        attributes.insert("d".to_string(), ast::NestedValue::Value("M".to_string()));
887        attributes.insert("i".to_string(), ast::NestedValue::Value("M".to_string()));
888        attributes.insert(
889            "passed".to_string(),
890            ast::NestedValue::Value("M".to_string()),
891        );
892        commands.push(ast::Command {
893            kind: ast::CommandType::Add,
894            object_kind: ast::ObjectKind::Overlay(
895                ast::OverlayType::Conformance(overlay_version.clone()),
896                ast::Content {
897                    attributes: Some(attributes),
898                    properties: None,
899                },
900            ),
901        });
902
903        let mut attributes = IndexMap::new();
904        let mut grouped_elements = IndexMap::new();
905        grouped_elements.insert(
906            "g1".to_string(),
907            ast::NestedValue::Array(vec![ast::NestedValue::Value("el1".to_string())]),
908        );
909        grouped_elements.insert(
910            "g2".to_string(),
911            ast::NestedValue::Array(vec![
912                ast::NestedValue::Value("el2".to_string()),
913                ast::NestedValue::Value("el3".to_string()),
914            ]),
915        );
916        attributes.insert(
917            "list".to_string(),
918            oca_ast_semantics::ast::NestedValue::Object(grouped_elements),
919        );
920        commands.push(ast::Command {
921            kind: ast::CommandType::Add,
922            object_kind: ast::ObjectKind::Overlay(
923                ast::OverlayType::EntryCode(overlay_version.clone()),
924                ast::Content {
925                    attributes: Some(attributes),
926                    properties: None,
927                },
928            ),
929        });
930
931        let oca_ast = ast::OCAAst {
932            version: "1.0".to_string(),
933            commands,
934            commands_meta: IndexMap::new(),
935            meta: HashMap::new(),
936        };
937
938        let build_result = from_ast(None, &oca_ast);
939        match build_result {
940            Ok(oca_build) => {
941                let code = HashFunctionCode::Blake3_256;
942                let format = SerializationFormats::JSON;
943                let oca_bundle_encoded = oca_build.oca_bundle.encode(&code, &format).unwrap();
944                let oca_bundle_json = String::from_utf8(oca_bundle_encoded).unwrap();
945                println!("{}", oca_bundle_json);
946            }
947            Err(e) => {
948                println!("{:?}", e);
949            }
950        }
951    }
952}