oca-dag 0.3.0-rc.3

Rust implementation of OCA DAG
Documentation
use oca_bundle::state::oca::overlay::unit::{ Unit, AttributeUnit, MeasurementSystem, MeasurementUnit, ImperialUnit, MetricUnit };
use oca_bundle::state::oca::overlay::format::Formats;
use oca_bundle::state::oca::overlay::entry::Entries;
use oca_bundle::state::oca::overlay::entry_code::EntryCodes;
use oca_bundle::state::oca::overlay::cardinality::Cardinalitys;
use oca_bundle::state::oca::overlay::conformance::Conformances;
use oca_bundle::state::oca::overlay::information::Information;
use oca_bundle::state::oca::overlay::character_encoding::CharacterEncodings;
use oca_bundle::state::oca::overlay::meta::Metas;
use oca_bundle::state::oca::overlay::label::Labels;
use std::str::FromStr;
use std::collections::HashMap;
use oca_ast::ast;
use oca_bundle::state::{oca::OCABox, oca::OCABundle, encoding::Encoding, attribute::{Attribute, AttributeType}, entry_codes::EntryCodes as EntryCodesValue, entries::EntriesElement};

pub fn apply_command(base: Option<OCABox>, op: ast::Command) -> OCABox {
    let mut digests: Vec<u8> = Vec::new();

    let mut oca: OCABox = match base {
        Some(oca) => oca,
        None => OCABox::new()
    };

    match op.kind {
        ast::CommandType::From => (),
        ast::CommandType::Add => {
            match op.object_kind {
                ast::ObjectKind::CaptureBase => {
                    if let Some(ref content) = op.content {
                        if let Some(ref attributes) = content.attributes {
                            for (attr_name, attr_type_value) in attributes {
                                let mut attribute = Attribute::new(attr_name.clone());
                                if let ast::NestedValue::Value(attr_value) = attr_type_value {
                                    if let Ok(attribute_type) = AttributeType::from_str(attr_value) {
                                        attribute.set_attribute_type(attribute_type);
                                    }
                                    oca.add_attribute(attribute);
                                }
                            }
                        }
                        if let Some(ref properties) = content.properties {
                            for (prop_name, prop_value) in properties {
                                if prop_name.eq("classification") {
                                    if let ast::NestedValue::Value(value) = prop_value {
                                        oca.add_classification(value.clone());
                                    }
                                }
                            }
                        }
                    }
                },
                ast::ObjectKind::Overlay(ref overlay_type) => {
                    match overlay_type {
                        ast::OverlayType::Meta => {
                            if let Some(ref content) = op.content {
                                if let Some(ref properties) = content.properties {
                                    let mut mut_properties = properties.clone();
                                    let lang = mut_properties.remove("lang");
                                    let mut lang_iso = None;
                                    if let Some(ast::NestedValue::Value(lang_str)) = lang {
                                        lang_iso = isolang::Language::from_639_1(lang_str.as_str());
                                    }

                                    for (prop_name, prop_value) in mut_properties {
                                        if let ast::NestedValue::Value(value) = prop_value {
                                            oca.add_meta(lang_iso.unwrap(), prop_name.clone(), value.clone());
                                        }
                                    }
                                }
                            }
                        },
                        ast::OverlayType::Label => {
                            if let Some(ref content) = op.content {
                                let mut lang_iso = None;
                                if let Some(ref properties) = content.properties {
                                    let mut mut_properties = properties.clone();
                                    let lang = mut_properties.remove("lang");
                                    if let Some(ast::NestedValue::Value(lang_str)) = lang {
                                        lang_iso = isolang::Language::from_639_1(lang_str.as_str());
                                    }
                                }
                                if let Some(ref attributes) = content.attributes {
                                    for (attr_name, attr_type_value) in attributes {
                                        let mut attribute = oca.attributes.get(attr_name).unwrap().clone();
                                        if let ast::NestedValue::Value(attr_label) = attr_type_value {
                                            attribute.set_label(lang_iso.unwrap(), attr_label.clone());
                                        }
                                        oca.add_attribute(attribute);
                                    }
                                }
                            }
                        },
                        ast::OverlayType::Information => {
                            if let Some(ref content) = op.content {
                                let mut lang_iso = None;
                                if let Some(ref properties) = content.properties {
                                    let mut mut_properties = properties.clone();
                                    let lang = mut_properties.remove("lang");
                                    if let Some(ast::NestedValue::Value(lang_str)) = lang {
                                        lang_iso = isolang::Language::from_639_1(lang_str.as_str());
                                    }
                                }
                                if let Some(ref attributes) = content.attributes {
                                    for (attr_name, attr_type_value) in attributes {
                                        let mut attribute = oca.attributes.get(attr_name).unwrap().clone();
                                        if let ast::NestedValue::Value(attr_info) = attr_type_value {
                                            attribute.set_information(lang_iso.unwrap(), attr_info.clone());
                                        }
                                        oca.add_attribute(attribute);
                                    }
                                }
                            }
                        },
                        ast::OverlayType::CharacterEncoding => {
                            if let Some(ref content) = op.content {
                                if let Some(ref attributes) = content.attributes {
                                    for (attr_name, attr_type_value) in attributes {
                                        let mut attribute = oca.attributes.get(attr_name).unwrap().clone();
                                        if let ast::NestedValue::Value(attr_encoding) = attr_type_value {
                                            attribute.set_encoding(Encoding::from_str(attr_encoding).unwrap());
                                        }
                                        oca.add_attribute(attribute);
                                    }
                                }
                            }
                        },
                        ast::OverlayType::Conformance => {
                            if let Some(ref content) = op.content {
                                if let Some(ref attributes) = content.attributes {
                                    for (attr_name, attr_type_value) in attributes {
                                        let mut attribute = oca.attributes.get(attr_name).unwrap().clone();
                                        if let ast::NestedValue::Value(attr_conformance) = attr_type_value {
                                            attribute.set_conformance(attr_conformance.clone());
                                        }
                                        oca.add_attribute(attribute);
                                    }
                                }
                            }
                        },
                        ast::OverlayType::Format => {
                            if let Some(ref content) = op.content {
                                if let Some(ref attributes) = content.attributes {
                                    for (attr_name, attr_type_value) in attributes {
                                        let mut attribute = oca.attributes.get(attr_name).unwrap().clone();
                                        if let ast::NestedValue::Value(attr_format) = attr_type_value {
                                            attribute.set_format(attr_format.clone());
                                        }
                                        oca.add_attribute(attribute);
                                    }
                                }
                            }
                        }
                        ast::OverlayType::Unit => {
                            if let Some(ref content) = op.content {
                                let mut unit_system_op = None;
                                if let Some(ref properties) = content.properties {
                                    let mut mut_properties = properties.clone();
                                    let unit_system_prop = mut_properties.remove("unit_system");
                                    if let Some(ast::NestedValue::Value(unit_system_str)) = unit_system_prop {
                                        unit_system_op = MeasurementSystem::from_str(&unit_system_str).ok();
                                    }
                                }
                                if let Some(unit_system) = unit_system_op {
                                    if let Some(ref attributes) = content.attributes {
                                        for (attr_name, attr_type_value) in attributes {
                                            let mut attribute = oca.attributes.get(attr_name).unwrap().clone();
                                            if let ast::NestedValue::Value(attr_unit) = attr_type_value {

                                                let mut unit = None;
                                                match unit_system {
                                                    MeasurementSystem::Metric => {
                                                        unit = Some(MeasurementUnit::Metric(MetricUnit::from_str(attr_unit).unwrap_or_else(|_| panic!("Invalid metric unit: {attr_unit}"))))
                                                    },
                                                    MeasurementSystem::Imperial => {
                                                        unit = Some(MeasurementUnit::Imperial(ImperialUnit::from_str(attr_unit).unwrap_or_else(|_| panic!("Invalid imperial unit: {attr_unit}"))))
                                                    }
                                                }
                                                attribute.set_unit(
                                                    AttributeUnit {
                                                        measurement_system: unit_system.clone(),
                                                        unit: unit.unwrap()
                                                    }
                                                );
                                            }
                                            oca.add_attribute(attribute);
                                        }
                                    }
                                }
                            }
                        }
                        ast::OverlayType::Cardinality => {
                            if let Some(ref content) = op.content {
                                if let Some(ref attributes) = content.attributes {
                                    for (attr_name, attr_type_value) in attributes {
                                        let mut attribute = oca.attributes.get(attr_name).unwrap().clone();
                                        if let ast::NestedValue::Value(attr_cardinality) = attr_type_value {
                                            attribute.set_cardinality(attr_cardinality.clone());
                                        }
                                        oca.add_attribute(attribute);
                                    }
                                }
                            }
                        },
                        ast::OverlayType::EntryCode => {
                            if let Some(ref content) = op.content {
                                if let Some(ref attributes) = content.attributes {
                                    for (attr_name, attr_type_value) in attributes {
                                        let mut attribute = oca.attributes.get(attr_name).unwrap().clone();
                                        match attr_type_value {
                                            ast::NestedValue::Value(attr_entry_codes_sai) => {
                                                attribute.set_entry_codes(EntryCodesValue::Sai(attr_entry_codes_sai.clone()));
                                            },
                                            ast::NestedValue::Array(attr_entry_codes) => {
                                                let mut entry_codes: Vec<String> = vec![];
                                                for attr_entry_code in attr_entry_codes {
                                                    if let ast::NestedValue::Value(entry_code) = attr_entry_code {
                                                        entry_codes.push(entry_code.clone());
                                                    }
                                                }
                                                attribute.set_entry_codes(EntryCodesValue::Array(entry_codes));
                                            },
                                            _ => ()

                                        }
                                        oca.add_attribute(attribute);
                                    }
                                }
                            }
                        },
                        ast::OverlayType::Entry => {
                            if let Some(ref content) = op.content {
                                let mut lang_iso = None;
                                if let Some(ref properties) = content.properties {
                                    let mut mut_properties = properties.clone();
                                    let lang = mut_properties.remove("lang");
                                    if let Some(ast::NestedValue::Value(lang_str)) = lang {
                                        lang_iso = isolang::Language::from_639_1(lang_str.as_str());
                                    }
                                }
                                if let Some(ref attributes) = content.attributes {
                                    for (attr_name, attr_type_value) in attributes {
                                        let mut attribute = oca.attributes.get(attr_name).unwrap().clone();
                                        match attr_type_value {
                                            ast::NestedValue::Value(attr_entries) => {
                                                attribute.set_entry(lang_iso.unwrap(), EntriesElement::Sai(attr_entries.clone()));
                                            }
                                            ast::NestedValue::Object(attr_entries) => {
                                                let mut entries = HashMap::new();
                                                for (attr_entry_key, attr_entry_value) in attr_entries {
                                                    if let ast::NestedValue::Value(entry_value) = attr_entry_value {
                                                        entries.insert(attr_entry_key.clone(), entry_value.clone());
                                                    }
                                                }
                                                attribute.set_entry(lang_iso.unwrap(), EntriesElement::Object(entries));
                                            },
                                            _ => ()
                                        }
                                        oca.add_attribute(attribute);
                                    }
                                }
                            }
                        }
                        _ => ()
                    }
                },
                _ => ()
            }
        },
        _ => ()
    }

    oca
}

#[cfg(test)]
mod tests {
    use super::*;
    use indexmap::IndexMap;

    #[test]
    #[ignore]
    fn test_add_step() {
        let mut commands = vec![];

        let mut attributes = IndexMap::new();
        attributes.insert("d".to_string(), ast::NestedValue::Value("Text".to_string()));
        attributes.insert("i".to_string(), ast::NestedValue::Value("Text".to_string()));
        attributes.insert("passed".to_string(), ast::NestedValue::Value("Boolean".to_string()));
        commands.push(
            ast::Command {
                kind: ast::CommandType::Add,
                object_kind: ast::ObjectKind::CaptureBase,
                content: Some(ast::Content {
                    attributes: Some(attributes),
                    properties: None,
                }),
            }
        );

        let mut properties = IndexMap::new();
        properties.insert("lang".to_string(), ast::NestedValue::Value("en".to_string()));
        properties.insert("name".to_string(), ast::NestedValue::Value("Entrance credential".to_string()));
        properties.insert("description".to_string(), ast::NestedValue::Value("Entrance credential".to_string()));
        commands.push(
            ast::Command {
                kind: ast::CommandType::Add,
                object_kind: ast::ObjectKind::Overlay(ast::OverlayType::Meta),
                content: Some(ast::Content {
                    attributes: None,
                    properties: Some(properties),
                }),
            }
        );

        let mut attributes = IndexMap::new();
        attributes.insert("d".to_string(), ast::NestedValue::Value("Schema digest".to_string()));
        attributes.insert("i".to_string(), ast::NestedValue::Value("Credential Issuee".to_string()));
        attributes.insert("passed".to_string(), ast::NestedValue::Value("Passed".to_string()));
        let mut properties = IndexMap::new();
        properties.insert("lang".to_string(), ast::NestedValue::Value("en".to_string()));
        commands.push(
            ast::Command {
                kind: ast::CommandType::Add,
                object_kind: ast::ObjectKind::Overlay(ast::OverlayType::Label),
                content: Some(ast::Content {
                    attributes: Some(attributes),
                    properties: Some(properties),
                }),
            }
        );

        let mut attributes = IndexMap::new();
        attributes.insert("d".to_string(), ast::NestedValue::Value("Schema digest".to_string()));
        attributes.insert("i".to_string(), ast::NestedValue::Value("Credential Issuee".to_string()));
        attributes.insert("passed".to_string(), ast::NestedValue::Value("Enables or disables passing".to_string()));
        let mut properties = IndexMap::new();
        properties.insert("lang".to_string(), ast::NestedValue::Value("en".to_string()));
        commands.push(
            ast::Command {
                kind: ast::CommandType::Add,
                object_kind: ast::ObjectKind::Overlay(ast::OverlayType::Information),
                content: Some(ast::Content {
                    attributes: Some(attributes),
                    properties: Some(properties),
                }),
            }
        );

        let mut attributes = IndexMap::new();
        attributes.insert("d".to_string(), ast::NestedValue::Value("utf-8".to_string()));
        attributes.insert("i".to_string(), ast::NestedValue::Value("utf-8".to_string()));
        attributes.insert("passed".to_string(), ast::NestedValue::Value("utf-8".to_string()));
        commands.push(
            ast::Command {
                kind: ast::CommandType::Add,
                object_kind: ast::ObjectKind::Overlay(ast::OverlayType::CharacterEncoding),
                content: Some(ast::Content {
                    attributes: Some(attributes),
                    properties: None,
                }),
            }
        );

        let mut attributes = IndexMap::new();
        attributes.insert("d".to_string(), ast::NestedValue::Value("M".to_string()));
        attributes.insert("i".to_string(), ast::NestedValue::Value("M".to_string()));
        attributes.insert("passed".to_string(), ast::NestedValue::Value("M".to_string()));
        commands.push(
            ast::Command {
                kind: ast::CommandType::Add,
                object_kind: ast::ObjectKind::Overlay(ast::OverlayType::Conformance),
                content: Some(ast::Content {
                    attributes: Some(attributes),
                    properties: None,
                }),
            }
        );

        let mut base: Option<OCABox> = None;
        for command in commands {
            let mut oca = apply_command(base, command);
            println!("{}", serde_json::to_string_pretty(&oca.generate_bundle()).unwrap());
            base = Some(oca);
        }
    }
}