use std::{cell::RefCell, collections::HashMap};
use dcbor::{
prelude::*,
walk::{EdgeType, WalkElement},
};
#[test]
fn test_traversal_counts() {
let array = CBOR::from(vec![1, 2, 3]);
let count = count_visits(&array);
assert_eq!(count, 4);
let mut map = Map::new();
map.insert("a", 1);
map.insert("b", 2);
let map_cbor = CBOR::from(map);
let count = count_visits(&map_cbor);
assert_eq!(count, 7);
let tagged = CBOR::to_tagged_value(42, 100);
let count = count_visits(&tagged);
assert_eq!(count, 2);
let mut inner_map = Map::new();
inner_map.insert("x", vec![1, 2]);
let mut outer_map = Map::new();
outer_map.insert("inner", inner_map);
outer_map.insert("simple", 42);
let nested = CBOR::from(outer_map);
let count = count_visits(&nested);
assert_eq!(count, 12);
}
#[test]
fn test_visitor_state_threading() {
let array = CBOR::from(vec![1, 2, 3, 4, 5]);
let even_count = RefCell::new(0);
let visitor = |element: &WalkElement,
_level: usize,
_edge: EdgeType,
state: ()|
-> ((), bool) {
match element {
WalkElement::Single(cbor) => {
if let CBORCase::Unsigned(n) = cbor.as_case()
&& n % 2 == 0
{
*even_count.borrow_mut() += 1;
}
}
WalkElement::KeyValue { .. } => {}
}
(state, false)
};
array.walk((), &visitor);
assert_eq!(*even_count.borrow(), 2); }
#[test]
fn test_early_termination() {
let nested_structure = CBOR::from(vec![
CBOR::from(vec!["should", "see", "this"]),
CBOR::from("abort_marker"), CBOR::from(vec!["should", "not", "see"]),
]);
let visit_log = RefCell::new(Vec::<String>::new());
let found_abort = RefCell::new(false);
let visitor = |element: &WalkElement,
level: usize,
edge: EdgeType,
state: ()|
-> ((), bool) {
let desc =
format!("L{}: {:?} - {}", level, edge, element.diagnostic_flat());
visit_log.borrow_mut().push(desc);
if let WalkElement::Single(cbor) = element
&& let CBORCase::Text(text) = cbor.as_case()
&& text == "abort_marker"
{
*found_abort.borrow_mut() = true;
return (state, true);
}
let stop = *found_abort.borrow()
&& matches!(element, WalkElement::Single(_))
&& matches!(edge, EdgeType::ArrayElement(2));
(state, stop)
};
nested_structure.walk((), &visitor);
let log = visit_log.borrow();
let log_str = log.join("\n");
assert!(log_str.contains("abort_marker"));
assert!(log_str.contains("should"));
assert!(log_str.contains("see"));
assert!(log_str.contains("this"));
assert!(log_str.contains("[\"should\", \"not\", \"see\"]"));
let log_lines: Vec<&str> = log.iter().map(|s| s.as_str()).collect();
let third_array_index = log_lines.iter().position(|line| {
line.contains("ArrayElement(2)")
&& line.contains("[\"should\", \"not\", \"see\"]")
});
if let Some(index) = third_array_index {
let visits_after_third_array = &log_lines[index + 1..];
let level2_after_third: Vec<&&str> = visits_after_third_array
.iter()
.filter(|line| line.starts_with("L2:"))
.collect();
assert!(
level2_after_third.is_empty(),
"Found unexpected level 2 visits after third array: {:?}",
level2_after_third
);
} else {
panic!("Could not find third array visit in log");
}
}
#[test]
fn test_depth_limited_traversal() {
let mut level3 = Map::new();
level3.insert("deep", "value");
let mut level2 = Map::new();
level2.insert("level3", level3);
let mut level1 = Map::new();
level1.insert("level2", level2);
let root = CBOR::from(level1);
let elements_by_level = RefCell::new(HashMap::<usize, usize>::new());
let visitor = |_element: &WalkElement,
level: usize,
_edge: EdgeType,
state: ()|
-> ((), bool) {
*elements_by_level.borrow_mut().entry(level).or_insert(0) += 1;
let stop = level >= 2;
(state, stop)
};
root.walk((), &visitor);
let counts = elements_by_level.borrow();
assert_eq!(*counts.get(&0).unwrap_or(&0), 1); assert_eq!(*counts.get(&1).unwrap_or(&0), 3); assert_eq!(*counts.get(&2).unwrap_or(&0), 1); assert_eq!(*counts.get(&3).unwrap_or(&0), 0); }
#[test]
fn test_text_extraction() {
let mut metadata = Map::new();
metadata.insert("title", "Important Document");
metadata.insert("author", "Alice Smith");
let mut content = Map::new();
content.insert("body", "Lorem ipsum dolor sit amet");
content.insert("footer", "Copyright 2024");
let mut document = Map::new();
document.insert("metadata", metadata);
document.insert("content", content);
document.insert("tags", vec!["urgent", "confidential", "draft"]);
let cbor = CBOR::from(document);
let texts = RefCell::new(Vec::<String>::new());
let visitor = |element: &WalkElement,
_level: usize,
_edge: EdgeType,
state: ()|
-> ((), bool) {
match element {
WalkElement::Single(cbor) => {
if let CBORCase::Text(text) = cbor.as_case() {
texts.borrow_mut().push(text.clone());
}
}
WalkElement::KeyValue { key, value } => {
if let CBORCase::Text(text) = key.as_case() {
texts.borrow_mut().push(text.clone());
}
if let CBORCase::Text(text) = value.as_case() {
texts.borrow_mut().push(text.clone());
}
}
}
(state, false)
};
cbor.walk((), &visitor);
let all_texts = texts.borrow();
assert!(all_texts.contains(&"Important Document".to_string()));
assert!(all_texts.contains(&"Alice Smith".to_string()));
assert!(all_texts.contains(&"Lorem ipsum dolor sit amet".to_string()));
assert!(all_texts.contains(&"Copyright 2024".to_string()));
assert!(all_texts.contains(&"urgent".to_string()));
assert!(all_texts.contains(&"confidential".to_string()));
assert!(all_texts.contains(&"draft".to_string()));
assert!(all_texts.contains(&"title".to_string()));
assert!(all_texts.contains(&"author".to_string()));
assert!(all_texts.contains(&"body".to_string()));
assert!(all_texts.contains(&"footer".to_string()));
assert!(all_texts.contains(&"metadata".to_string()));
assert!(all_texts.contains(&"content".to_string()));
assert!(all_texts.contains(&"tags".to_string()));
}
#[test]
fn test_traversal_order_and_edge_types() {
let mut map = Map::new();
map.insert("a", vec![1, 2]);
map.insert("b", 42);
let cbor = CBOR::from(map);
let traversal_log = RefCell::new(Vec::<(String, EdgeType)>::new());
let visitor = |element: &WalkElement,
_level: usize,
edge: EdgeType,
state: ()|
-> ((), bool) {
let desc = match element {
WalkElement::Single(cbor) => {
format!("Single({})", cbor.diagnostic_flat())
}
WalkElement::KeyValue { key, value } => {
format!(
"KeyValue({}: {})",
key.diagnostic_flat(),
value.diagnostic_flat()
)
}
};
traversal_log.borrow_mut().push((desc, edge));
(state, false)
};
cbor.walk((), &visitor);
let log = traversal_log.borrow();
assert_eq!(log[0].1, EdgeType::None);
let edge_types: Vec<EdgeType> = log.iter().map(|(_, edge)| *edge).collect();
assert!(edge_types.contains(&EdgeType::MapKeyValue));
assert!(edge_types.contains(&EdgeType::MapKey));
assert!(edge_types.contains(&EdgeType::MapValue));
assert!(edge_types.contains(&EdgeType::ArrayElement(0)));
assert!(edge_types.contains(&EdgeType::ArrayElement(1)));
}
#[test]
fn test_tagged_value_traversal() {
let inner_tagged = CBOR::to_tagged_value(123, vec![1, 2, 3]);
let outer_tagged = CBOR::to_tagged_value(456, inner_tagged);
let edge_log = RefCell::new(Vec::<EdgeType>::new());
let visitor = |_element: &WalkElement,
_level: usize,
edge: EdgeType,
state: ()|
-> ((), bool) {
edge_log.borrow_mut().push(edge);
(state, false)
};
outer_tagged.walk((), &visitor);
let edges = edge_log.borrow();
assert_eq!(edges[0], EdgeType::None); assert_eq!(edges[1], EdgeType::TaggedContent); assert_eq!(edges[2], EdgeType::TaggedContent); assert_eq!(edges[3], EdgeType::ArrayElement(0)); assert_eq!(edges[4], EdgeType::ArrayElement(1)); assert_eq!(edges[5], EdgeType::ArrayElement(2)); }
#[test]
fn test_map_keyvalue_semantics() {
let mut map = Map::new();
map.insert("simple", 42);
map.insert("nested", vec![1, 2]);
let cbor = CBOR::from(map);
let keyvalue_count = RefCell::new(0);
let individual_count = RefCell::new(0);
let visitor = |element: &WalkElement,
_level: usize,
edge: EdgeType,
state: ()|
-> ((), bool) {
match element {
WalkElement::KeyValue { .. } => {
*keyvalue_count.borrow_mut() += 1;
assert_eq!(edge, EdgeType::MapKeyValue);
}
WalkElement::Single(_) => {
if matches!(edge, EdgeType::MapKey | EdgeType::MapValue) {
*individual_count.borrow_mut() += 1;
}
}
}
(state, false)
};
cbor.walk((), &visitor);
assert_eq!(*keyvalue_count.borrow(), 2);
assert_eq!(*individual_count.borrow(), 4);
}
#[test]
fn test_stop_flag_prevents_descent() {
let nested = CBOR::from(vec![
vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9], ]);
let visit_log = RefCell::new(Vec::<String>::new());
let visitor = |element: &WalkElement,
level: usize,
edge: EdgeType,
state: ()|
-> ((), bool) {
let desc =
format!("L{}: {:?} - {}", level, edge, element.diagnostic_flat());
visit_log.borrow_mut().push(desc);
let stop = level == 1 && matches!(edge, EdgeType::ArrayElement(0));
(state, stop)
};
nested.walk((), &visitor);
let log = visit_log.borrow();
let log_str = log.join("\n");
assert!(log_str.contains("ArrayElement(0) - [1, 2, 3]"));
let level2_lines: Vec<&str> = log
.iter()
.filter(|line| line.starts_with("L2:"))
.map(|s| s.as_str())
.collect();
for line in &level2_lines {
assert!(!line.contains(" - 1"));
assert!(!line.contains(" - 2"));
assert!(!line.contains(" - 3"));
}
assert!(log_str.contains("ArrayElement(1) - [4, 5, 6]")); assert!(log_str.contains("ArrayElement(2) - [7, 8, 9]"));
assert!(
log_str.contains("L2:")
&& (log_str.contains(" - 4")
|| log_str.contains(" - 5")
|| log_str.contains(" - 6"))
);
assert!(
log_str.contains("L2:")
&& (log_str.contains(" - 7")
|| log_str.contains(" - 8")
|| log_str.contains(" - 9"))
);
}
#[test]
fn test_empty_structures() {
let empty_array = CBOR::from(Vec::<i32>::new());
let count = count_visits(&empty_array);
assert_eq!(count, 1);
let empty_map = CBOR::from(Map::new());
let count = count_visits(&empty_map);
assert_eq!(count, 1); }
#[test]
fn test_primitive_values() {
let primitives = vec![
CBOR::from(42),
CBOR::from("hello"),
CBOR::from(3.2222),
CBOR::from(true),
CBOR::null(),
];
for primitive in primitives {
let count = count_visits(&primitive);
assert_eq!(count, 1); }
}
#[test]
fn test_real_world_document() {
let mut person = Map::new();
person.insert("name", "John Doe");
person.insert("age", 30);
person.insert("email", "john@example.com");
let mut address = Map::new();
address.insert("street", "123 Main St");
address.insert("city", "Anytown");
address.insert("zipcode", "12345");
person.insert("address", address);
person.insert("hobbies", vec!["reading", "cycling", "cooking"]);
let mut skills = Map::new();
skills.insert("programming", vec!["Rust", "Python", "JavaScript"]);
skills.insert("languages", vec!["English", "Spanish"]);
person.insert("skills", skills);
let document = CBOR::from(person);
let strings = RefCell::new(Vec::<String>::new());
let visitor = |element: &WalkElement,
_level: usize,
_edge: EdgeType,
state: ()|
-> ((), bool) {
match element {
WalkElement::Single(cbor) => {
if let CBORCase::Text(text) = cbor.as_case() {
strings.borrow_mut().push(text.clone());
}
}
WalkElement::KeyValue { key, value } => {
if let CBORCase::Text(text) = key.as_case() {
strings.borrow_mut().push(text.clone());
}
if let CBORCase::Text(text) = value.as_case() {
strings.borrow_mut().push(text.clone());
}
}
}
(state, false)
};
document.walk((), &visitor);
let all_strings = strings.borrow();
assert!(all_strings.contains(&"John Doe".to_string()));
assert!(all_strings.contains(&"john@example.com".to_string()));
assert!(all_strings.contains(&"123 Main St".to_string()));
assert!(all_strings.contains(&"Anytown".to_string()));
assert!(all_strings.contains(&"12345".to_string()));
assert!(all_strings.contains(&"reading".to_string()));
assert!(all_strings.contains(&"cycling".to_string()));
assert!(all_strings.contains(&"cooking".to_string()));
assert!(all_strings.contains(&"Rust".to_string()));
assert!(all_strings.contains(&"Python".to_string()));
assert!(all_strings.contains(&"JavaScript".to_string()));
assert!(all_strings.contains(&"English".to_string()));
assert!(all_strings.contains(&"Spanish".to_string()));
assert!(all_strings.contains(&"name".to_string()));
assert!(all_strings.contains(&"age".to_string()));
assert!(all_strings.contains(&"email".to_string()));
assert!(all_strings.contains(&"address".to_string()));
assert!(all_strings.contains(&"hobbies".to_string()));
assert!(all_strings.contains(&"skills".to_string()));
assert!(all_strings.contains(&"programming".to_string()));
assert!(all_strings.contains(&"languages".to_string()));
}
fn count_visits(cbor: &CBOR) -> usize {
let count = RefCell::new(0);
let visitor = |_element: &WalkElement,
_level: usize,
_edge: EdgeType,
state: ()|
-> ((), bool) {
*count.borrow_mut() += 1;
(state, false)
};
cbor.walk((), &visitor);
*count.borrow()
}