Skip to content

Commit

Permalink
feat: extract nested attributes from oca-file
Browse files Browse the repository at this point in the history
  • Loading branch information
edytapawlak authored and mitfik committed Jan 1, 2024
1 parent 3bb8396 commit a700d5f
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 123 deletions.
4 changes: 2 additions & 2 deletions oca-ast/src/ast/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<NestedAttrType>),
Reference(RefValue),
Value(AttributeType),
Object(IndexMap<String, NestedAttrType>),
#[serde(serialize_with = "array_serializer")]
Array(Box<NestedAttrType>),
/// Indicator that attribute was removed and does not need any type
Null,
}
Expand Down
2 changes: 0 additions & 2 deletions oca-ast/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -939,8 +939,6 @@ impl<'de> Deserialize<'de> for ObjectKind {

#[cfg(test)]
mod tests {
use log::debug;

use super::*;

#[test]
Expand Down
1 change: 0 additions & 1 deletion oca-file/src/ocafile/instructions/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
173 changes: 61 additions & 112 deletions oca-file/src/ocafile/instructions/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Pair> {
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);
Expand All @@ -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);
}
}
}
Expand Down
29 changes: 23 additions & 6 deletions oca-file/src/ocafile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>().join(", ");
vec![start, out, end].join("")
}
Expand Down Expand Up @@ -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
"#;
Expand All @@ -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();
Expand All @@ -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})]");
}
}

0 comments on commit a700d5f

Please sign in to comment.