diff --git a/oca-ast/src/ast/attributes.rs b/oca-ast/src/ast/attributes.rs index 61b2323..c9b6da3 100644 --- a/oca-ast/src/ast/attributes.rs +++ b/oca-ast/src/ast/attributes.rs @@ -22,11 +22,11 @@ use super::{AttributeType, RefValue}; /// Object: can be inline object which can have nested attributes types /// Array: is an array of specific type (only one type allowed) pub enum NestedAttrType { - #[serde(serialize_with = "array_serializer")] - Array(Box), Reference(RefValue), Value(AttributeType), Object(IndexMap), + #[serde(serialize_with = "array_serializer")] + Array(Box), /// Indicator that attribute was removed and does not need any type Null, } diff --git a/oca-ast/src/ast/mod.rs b/oca-ast/src/ast/mod.rs index 10f0d1b..0f842fd 100644 --- a/oca-ast/src/ast/mod.rs +++ b/oca-ast/src/ast/mod.rs @@ -939,8 +939,6 @@ impl<'de> Deserialize<'de> for ObjectKind { #[cfg(test)] mod tests { - use log::debug; - use super::*; #[test] diff --git a/oca-file/src/ocafile/instructions/add.rs b/oca-file/src/ocafile/instructions/add.rs index fa7663d..68d2589 100644 --- a/oca-file/src/ocafile/instructions/add.rs +++ b/oca-file/src/ocafile/instructions/add.rs @@ -29,7 +29,6 @@ impl AddInstruction { { info!("Parsed attribute: {:?} = {:?}", key, value); - // TODO find out how to parse nested objects attributes.insert(key, value); } else { debug!("Attribute skipped"); diff --git a/oca-file/src/ocafile/instructions/helpers.rs b/oca-file/src/ocafile/instructions/helpers.rs index 1c11311..90da1c6 100644 --- a/oca-file/src/ocafile/instructions/helpers.rs +++ b/oca-file/src/ocafile/instructions/helpers.rs @@ -2,10 +2,67 @@ use std::str::FromStr; use indexmap::IndexMap; use log::debug; -use oca_ast::ast::{NestedValue, AttributeType, NestedAttrType, Content, RefValue}; +use oca_ast::ast::{NestedValue, AttributeType, NestedAttrType, Content, RefValue, attributes::NestedAttrTypeFrame}; +use recursion::ExpandableExt; use said::SelfAddressingIdentifier; use crate::ocafile::{Pair, Rule}; +fn extract(input: Pair) -> NestedAttrType { + NestedAttrType::expand_frames(input, |seed| { + match seed.as_rule() { + Rule::_attr_type => { + let mut inner = seed.into_inner(); + let inner_pair = inner.next().unwrap(); + match inner_pair.as_rule() { + Rule::base_attr_type => { + let attr_type = AttributeType::from_str(inner_pair.as_span().as_str()).unwrap(); + NestedAttrTypeFrame::Value(attr_type) + }, + Rule::object_attr_type => { + NestedAttrTypeFrame::Object(extract_object(inner_pair)) + }, + Rule::array_attr_type => { + NestedAttrTypeFrame::Array(inner_pair.into_inner().next().unwrap()) + }, + _ => todo!() + + } + }, + Rule::base_attr_type => { + let attr_type = AttributeType::from_str(seed.as_span().as_str()).unwrap(); + NestedAttrTypeFrame::Value(attr_type) + }, + Rule::reference => { + NestedAttrTypeFrame::Reference(oca_ast::ast::RefValue::Name(seed.as_str().to_string())) + }, + Rule::said => { + let said = SelfAddressingIdentifier::from_str(seed.as_str()).unwrap(); + NestedAttrTypeFrame::Reference(RefValue::Said(said)) + }, + Rule::object_attr_type => { + NestedAttrTypeFrame::Object(extract_object(seed)) + }, + Rule::array_attr_type => { + NestedAttrTypeFrame::Array(seed) + }, + r => { + panic!("Matching attr type didn't work. Unhandled Rule type: {:?}", r); + } + } + }) +} + +fn extract_object(input_pair: Pair) -> IndexMap { + let mut object_fields = input_pair.into_inner(); + let mut idmap = IndexMap::new(); + while let Some(field) = object_fields.next() { + let key = field.as_span().as_str().to_owned(); + let value = object_fields.next().unwrap(); + idmap.insert(key, value); + }; + idmap +} + pub fn extract_attribute_type(attr_pair: Pair) -> Option<(String, NestedAttrType)> { let mut attr_name = String::new(); let mut attr_type = NestedAttrType::Value(AttributeType::Text); @@ -14,120 +71,12 @@ pub fn extract_attribute_type(attr_pair: Pair) -> Option<(String, NestedAttrType for item in attr_pair.into_inner() { match item.as_rule() { Rule::attr_key => { - attr_name = item.as_str().to_string(); debug!("Extracting attribute key {:?}", attr_name); - }, - Rule::object_attr_type => { - // TODO hack to make it work for ARRAY needs to be solved properly - debug!("Matching object attribute type from rule: {:?}", item); - let mut entries = IndexMap::new(); - // TODO recurently parse nested objects - // Currently extract_attribute_type fn does not handle nested objects, - // ita always overwrites the attr - let (entry_key, entry_value) = extract_attribute_type(item).unwrap(); - entries.insert(entry_key, entry_value); - attr_type = NestedAttrType::Object(entries); - }, - Rule::_attr_type => { - debug!("Attribute type to parse: {:?}", item); - if let Some(attr_type_rule) = item.clone().into_inner().next() { - match attr_type_rule.as_rule() { - Rule::reference => { - debug!("Matching referance {:?}", attr_type_rule); - attr_type = NestedAttrType::Reference(oca_ast::ast::RefValue::Name(attr_type_rule.as_str().to_string())); - }, - Rule::said => { - debug!("Matching said reference: {:?}", attr_type_rule); - let said = SelfAddressingIdentifier::from_str(attr_type_rule.as_str()).unwrap(); - attr_type = NestedAttrType::Reference(RefValue::Said(said)); - } - Rule::base_attr_type => { - debug!("Matching basic attribute type from rule: {}", attr_type_rule); - match AttributeType::from_str(attr_type_rule.as_span().as_str()) { - Ok(base_attr_type) => { - debug!("Attribute type: {:?}", base_attr_type); - attr_type = NestedAttrType::Value(base_attr_type); - } - Err(e) => { - panic!("Invalid attribute type {:?}", e); - } - } - } - Rule::array_attr_type => { - debug!("Matching array attribute type from rule: {:?}", attr_type_rule); - // TODO hack: First try basic type if doesn not work try to extract next level. - for temp_attr_type in attr_type_rule.clone().into_inner() { - match temp_attr_type.as_rule() { - Rule::base_attr_type => { - match AttributeType::from_str(temp_attr_type.as_span().as_str()) { - Ok(base_attr_type) => { - debug!("Attribute type: {:?}", base_attr_type); - attr_type = NestedAttrType::Value(base_attr_type); - } - Err(e) => { - panic!("Invalid attribute type {:?}", e); - } - } - } - _ => { - if let Some((_, inner_type)) = extract_attribute_type(temp_attr_type.clone()) { - // TODO recursion needed - match inner_type { - NestedAttrType::Value(base_attr_type) => { - attr_type = NestedAttrType::Array(Box::new(NestedAttrType::Value(base_attr_type))); - } - NestedAttrType::Reference(ref_value) => { - attr_type = NestedAttrType::Array(Box::new(NestedAttrType::Reference(ref_value))); - } - NestedAttrType::Object(entries) => { - attr_type = NestedAttrType::Array(Box::new(NestedAttrType::Object(entries))); - } - NestedAttrType::Array(box_attr_type) => { - attr_type = NestedAttrType::Array(Box::new(NestedAttrType::Array(box_attr_type))); - }, - NestedAttrType::Null => todo!(), - } - } - } - } - } - - } - Rule::ref_array => { - debug!("Matching reference array type from rule: {:?}", attr_type_rule); - if let Some(value) = attr_type_rule.clone().into_inner().next() { - match value.as_rule() { - Rule::reference => { - attr_type = NestedAttrType::Array(Box::new(NestedAttrType::Reference(oca_ast::ast::RefValue::Name(value.as_str().to_string())))); - }, - Rule::said => { - let said = SelfAddressingIdentifier::from_str(value.as_str()).unwrap(); // TODO - attr_type = NestedAttrType::Array(Box::new(NestedAttrType::Reference(RefValue::Said(said)))); - }, - _ => { - panic!("Invalid reference array value in {:?}", value.as_rule()); - } - } - } - } - Rule::object_attr_type => { - debug!("Matching object attribute type from rule: {:?}", attr_type_rule); - let mut entries = IndexMap::new(); - // TODO recurently parse nested objects - // Currently extract_attribute_type fn does not handle nested objects, - // ita always overwrites the attr - let (entry_key, entry_value) = extract_attribute_type(attr_type_rule).unwrap(); - entries.insert(entry_key, entry_value); - attr_type = NestedAttrType::Object(entries); - } - _ => { - panic!("Matching attr type didn't worked"); - } - } - } + attr_name = item.as_str().to_string(); }, _ => { - panic!("Invalid attribute in {:?}", item.as_rule()); + debug!("Attribute type to parse: {:?}", item); + attr_type = extract(item); } } } diff --git a/oca-file/src/ocafile/mod.rs b/oca-file/src/ocafile/mod.rs index a225f42..aee4a06 100644 --- a/oca-file/src/ocafile/mod.rs +++ b/oca-file/src/ocafile/mod.rs @@ -166,11 +166,11 @@ fn oca_file_format(nested: NestedAttrType) -> String { format!("{}", value) } NestedAttrTypeFrame::Object(obj) => { - let start = "Object {".to_string(); - let end = "}".to_string(); + let start = "Object({".to_string(); + let end = "})".to_string(); let inner_data = obj .into_iter() - .map(|(obj_key, obj_value)| format!(" {}={}", obj_key, obj_value)); + .map(|(obj_key, obj_value)| format!("{}={}", obj_key, obj_value)); let out = inner_data.collect::>().join(", "); vec![start, out, end].join("") } @@ -461,7 +461,7 @@ ADD ENTRY pl ATTRS radio={"o1": "etykieta1", "o2": "etykieta2", "o3": "etykieta3 } #[test] - fn test_deserialization_ast_to_ocafile_attributes() { + fn test_attributes_from_ast_to_ocafile() { let unparsed_file = r#"ADD ATTRIBUTE name=Text age=Numeric ADD ATTRIBUTE list=Array[Text] el=Text "#; @@ -475,6 +475,24 @@ ADD ATTRIBUTE list=Array[Text] el=Text ); } + #[test] + fn test_nested_attributes_from_ocafile_to_ast() { + let unparsed_file = +r#"ADD ATTRIBUTE name=Text age=Numeric car=Object({vin=Text, model=Text, year=Numeric}) +ADD ATTRIBUTE incidentals_spare_parts=Array[Object({part_number=Text, description=Text, unit=Text, quantity=Numeric})] +"#; + let oca_ast = parse_from_string(unparsed_file.to_string()).unwrap(); + + let ocafile = generate_from_ast(&oca_ast); + assert_eq!( + ocafile, unparsed_file, + "left:\n{} \n right:\n {}", + ocafile, unparsed_file + ); + + } + + #[test] fn test_oca_file_format() { let mut object_example = IndexMap::new(); @@ -496,7 +514,6 @@ ADD ATTRIBUTE list=Array[Text] el=Text let attr = NestedAttrType::Array(Box::new(NestedAttrType::Object(object_example))); let out = oca_file_format(attr); - assert_eq!(out, "Array[Object { name=Text, age=Numeric, data=refs:EJeWVGxkqxWrdGi0efOzwg1YQK8FrA-ZmtegiVEtAVcu}]"); - println!("{}", out); + assert_eq!(out, "Array[Object({name=Text, age=Numeric, data=refs:EJeWVGxkqxWrdGi0efOzwg1YQK8FrA-ZmtegiVEtAVcu})]"); } }