diff --git a/lib/src/node.rs b/lib/src/node.rs index a87cc22..67fc470 100644 --- a/lib/src/node.rs +++ b/lib/src/node.rs @@ -206,8 +206,17 @@ impl Node { } pub fn create_hierarchy(&mut self, path: &str) -> &mut Node { + self.create_hierarchy_from_iter(path.split(".")) + } + + pub(crate) fn create_hierarchy_from_iter(&mut self, path: I) -> &mut Node + where + I: IntoIterator, + S: AsRef, + { let mut ptr = self; - for name in path.split('.') { + for name in path { + let name = name.as_ref(); if ptr.get(name).is_none() { ptr.add(Node::section(name)); } @@ -218,19 +227,6 @@ impl Node { ptr } - pub fn create_record_hierarchy(&mut self, path: &str) -> &mut Node { - match path.split_once(".") { - Some((record, hierarchy)) => { - if self.get(record).is_none() { - self.add(Node::record(record)); - } - let record_node = self.get_mut(record).expect("Record node should be present"); - record_node.create_hierarchy(hierarchy) - } - None => self.create_hierarchy(path), - } - } - /// Returns an iterator over the node's children. The children nodes are sorted alphabetically. /// /// # Examples diff --git a/lib/src/record/decode.rs b/lib/src/record/decode.rs index 8c578e5..bf80f2c 100644 --- a/lib/src/record/decode.rs +++ b/lib/src/record/decode.rs @@ -9,7 +9,7 @@ use crate::header::record_types; use crate::node::Node; use crate::node::NodeType; #[cfg(not(feature = "std"))] -use alloc::{str, string::String, vec::Vec}; +use alloc::{borrow::ToOwned, str, string::String, vec::Vec}; use log::debug; #[cfg(feature = "std")] use std::str; @@ -60,6 +60,7 @@ impl Record { let csv = str::from_utf8(layout)?; let mut columns = Vec::new(); + let mut current_path = Vec::new(); for (i, line) in csv.lines().enumerate() { if i == 0 { @@ -85,7 +86,32 @@ impl Record { if entry.name.is_empty() { continue; } - let node = record_root.create_record_hierarchy(&entry.name); + + let mut segments = entry.name.split("."); + let Some(top) = segments.next() else { + continue; + }; + + if !top.is_empty() { + // Absolute path + current_path.clear(); + current_path.push(top.to_owned()); + + if record_root.get(top).is_none() { + // Top-level is assumed to be the record name + record_root.add(Node::record(top)); + } + } + + for segment in segments { + if segment.is_empty() { + let _ = current_path.pop(); + } else { + current_path.push(segment.to_owned()); + } + } + + let node = record_root.create_hierarchy_from_iter(¤t_path); node.description = entry.description; if let Some(value) = self.read_field(offset * 8 + entry.offset, entry.size) { node.kind = NodeType::Field { value } diff --git a/lib/tests/node.rs b/lib/tests/node.rs index 5a659ec..d36d12a 100644 --- a/lib/tests/node.rs +++ b/lib/tests/node.rs @@ -89,11 +89,13 @@ fn merge() { #[test] fn merge_record() { let mut root0 = Node::root(); - root0.create_record_hierarchy("foo.bar.reg0"); + root0.add(Node::record("foo")); + root0.create_hierarchy("foo.bar.reg0"); root0.create_hierarchy("some.bar.reg1"); let mut root1 = Node::root(); - root1.create_record_hierarchy("foo.bar.reg1"); + root1.add(Node::record("foo")); + root1.create_hierarchy("foo.bar.reg1"); root0.merge(root1); diff --git a/lib/tests/record.rs b/lib/tests/record.rs index 2475f9a..d8b4523 100644 --- a/lib/tests/record.rs +++ b/lib/tests/record.rs @@ -24,7 +24,7 @@ foo.bar;4;8;;0"; let root = record.decode_with_csv(csv.as_bytes(), 0).unwrap(); let section = root.get_by_path("foo").unwrap(); - assert_eq!(section.kind, NodeType::Section); + assert_eq!(section.kind, NodeType::Record); let field = root.get_by_path("foo.bar").unwrap(); assert_eq!(field.kind, NodeType::Field { value: 0x18 }); let field = root.get_by_path("foo.bar.baz").unwrap(); @@ -36,6 +36,42 @@ foo.bar;4;8;;0"; ); } +#[test] +fn relative_paths() { + let record = Record { + header: Header::default(), + data: vec![ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, + 0x8E, 0x8F, + ], + }; + + let csv = "name;offset;size;description;bitfield +foo;0;128;;0 +.aaa;8;8;;0 +.bbb;16;8;;0 +..ccc;24;8;;0 +foo.ddd.eee;32;8;;0 +...ddd;40;8;;0 +..fff;48;8;;0"; + + let root = record.decode_with_csv(csv.as_bytes(), 0).unwrap(); + let section = root.get_by_path("foo").unwrap(); + assert_eq!(section.kind, NodeType::Record); + let field = root.get_by_path("foo.aaa").unwrap(); + assert_eq!(field.kind, NodeType::Field { value: 0x81 }); + let field = root.get_by_path("foo.aaa.bbb").unwrap(); + assert_eq!(field.kind, NodeType::Field { value: 0x82 }); + let field = root.get_by_path("foo.aaa.ccc").unwrap(); + assert_eq!(field.kind, NodeType::Field { value: 0x83 }); + let field = root.get_by_path("foo.ddd.eee").unwrap(); + assert_eq!(field.kind, NodeType::Field { value: 0x84 }); + let field = root.get_by_path("foo.ddd").unwrap(); + assert_eq!(field.kind, NodeType::Field { value: 0x85 }); + let field = root.get_by_path("foo.fff").unwrap(); + assert_eq!(field.kind, NodeType::Field { value: 0x86 }); +} + #[test] fn decode() { let mut cm = CollateralManager::file_system_tree(Path::new(COLLATERAL_TREE_PATH)).unwrap();