import_stdlib!();
use crate::{CBOR, CBORCase};
#[derive(Debug, Clone)]
pub enum WalkElement {
Single(CBOR),
KeyValue { key: CBOR, value: CBOR },
}
impl WalkElement {
pub fn as_single(&self) -> Option<&CBOR> {
match self {
WalkElement::Single(cbor) => Some(cbor),
WalkElement::KeyValue { .. } => None,
}
}
pub fn as_key_value(&self) -> Option<(&CBOR, &CBOR)> {
match self {
WalkElement::Single(_) => None,
WalkElement::KeyValue { key, value } => Some((key, value)),
}
}
pub fn diagnostic_flat(&self) -> String {
match self {
WalkElement::Single(cbor) => cbor.diagnostic_flat(),
WalkElement::KeyValue { key, value } => {
format!(
"{}: {}",
key.diagnostic_flat(),
value.diagnostic_flat()
)
}
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum EdgeType {
None,
ArrayElement(usize),
MapKeyValue,
MapKey,
MapValue,
TaggedContent,
}
impl EdgeType {
pub fn label(&self) -> Option<String> {
match self {
EdgeType::ArrayElement(index) => Some(format!("arr[{}]", index)),
EdgeType::MapKeyValue => Some("kv".to_string()),
EdgeType::MapKey => Some("key".to_string()),
EdgeType::MapValue => Some("val".to_string()),
EdgeType::TaggedContent => Some("content".to_string()),
EdgeType::None => None,
}
}
}
pub type Visitor<'a, State> =
dyn Fn(&WalkElement, usize, EdgeType, State) -> (State, bool) + 'a;
impl CBOR {
pub fn walk<State: Clone>(&self, state: State, visit: &Visitor<'_, State>) {
self._walk(0, EdgeType::None, state, visit);
}
fn _walk<State: Clone>(
&self,
level: usize,
incoming_edge: EdgeType,
state: State,
visit: &Visitor<'_, State>,
) {
let mut state = state;
let stop;
let element = WalkElement::Single(self.clone());
(state, stop) = visit(&element, level, incoming_edge, state);
if stop {
return;
}
let next_level = level + 1;
match self.as_case() {
CBORCase::Array(array) => {
for (index, element) in array.iter().enumerate() {
element._walk(
next_level,
EdgeType::ArrayElement(index),
state.clone(),
visit,
);
}
}
CBORCase::Map(map) => {
for (key, value) in map.iter() {
let kv_element = WalkElement::KeyValue {
key: key.clone(),
value: value.clone(),
};
let (new_state, stop) = visit(
&kv_element,
next_level,
EdgeType::MapKeyValue,
state.clone(),
);
if stop {
continue; }
key._walk(
next_level,
EdgeType::MapKey,
new_state.clone(),
visit,
);
value._walk(
next_level,
EdgeType::MapValue,
new_state,
visit,
);
}
}
CBORCase::Tagged(_tag, content) => {
content._walk(
next_level,
EdgeType::TaggedContent,
state,
visit,
);
}
CBORCase::Unsigned(_)
| CBORCase::Negative(_)
| CBORCase::ByteString(_)
| CBORCase::Text(_)
| CBORCase::Simple(_) => {
}
}
}
}
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use super::*;
use crate::Map;
#[test]
fn test_walk_simple_value() {
let cbor = CBOR::from(42);
let count = RefCell::new(0);
let visitor = |_element: &WalkElement,
_level: usize,
_edge: EdgeType,
state: ()|
-> ((), bool) {
*count.borrow_mut() += 1;
(state, false)
};
cbor.walk((), &visitor);
assert_eq!(*count.borrow(), 1);
}
#[test]
fn test_walk_array() {
let cbor = CBOR::from(vec![1, 2, 3]);
let count = RefCell::new(0);
let edges = RefCell::new(Vec::new());
let visitor = |_element: &WalkElement,
_level: usize,
edge: EdgeType,
state: ()|
-> ((), bool) {
*count.borrow_mut() += 1;
edges.borrow_mut().push(edge);
(state, false)
};
cbor.walk((), &visitor);
assert_eq!(*count.borrow(), 4);
let edges = edges.borrow();
assert_eq!(edges[0], EdgeType::None); assert_eq!(edges[1], EdgeType::ArrayElement(0)); assert_eq!(edges[2], EdgeType::ArrayElement(1)); assert_eq!(edges[3], EdgeType::ArrayElement(2)); }
#[test]
fn test_walk_map() {
let mut map = Map::new();
map.insert("key1", "value1");
map.insert("key2", "value2");
let cbor = CBOR::from(map);
let count = RefCell::new(0);
let edges = RefCell::new(Vec::new());
let visitor = |_element: &WalkElement,
_level: usize,
edge: EdgeType,
state: ()|
-> ((), bool) {
*count.borrow_mut() += 1;
edges.borrow_mut().push(edge);
(state, false)
};
cbor.walk((), &visitor);
assert_eq!(*count.borrow(), 7);
let edges = edges.borrow();
assert_eq!(edges[0], EdgeType::None); assert!(edges.contains(&EdgeType::MapKeyValue));
assert!(edges.contains(&EdgeType::MapKey));
assert!(edges.contains(&EdgeType::MapValue));
}
#[test]
fn test_walk_map_with_nested_content() {
let mut map = Map::new();
map.insert("simple", "value");
map.insert("nested", vec![1, 2, 3]); let cbor = CBOR::from(map);
let count = RefCell::new(0);
let edges = RefCell::new(Vec::new());
let visitor = |_element: &WalkElement,
_level: usize,
edge: EdgeType,
state: ()|
-> ((), bool) {
*count.borrow_mut() += 1;
edges.borrow_mut().push(edge);
(state, false)
};
cbor.walk((), &visitor);
let edges = edges.borrow();
assert_eq!(edges[0], EdgeType::None); assert!(edges.contains(&EdgeType::MapKeyValue)); assert!(edges.contains(&EdgeType::MapValue)); assert!(edges.contains(&EdgeType::ArrayElement(0))); }
#[test]
fn test_walk_key_value_pairs() {
let mut map = Map::new();
map.insert("name", "Alice");
map.insert("age", 30);
let cbor = CBOR::from(map);
let key_value_pairs = RefCell::new(Vec::new());
let visitor = |element: &WalkElement,
_level: usize,
_edge: EdgeType,
state: ()|
-> ((), bool) {
if let Some((key, value)) = element.as_key_value()
&& let (CBORCase::Text(k), _) = (key.as_case(), value.as_case())
{
key_value_pairs.borrow_mut().push(k.clone());
}
(state, false)
};
cbor.walk((), &visitor);
let pairs = key_value_pairs.borrow();
assert_eq!(pairs.len(), 2);
assert!(pairs.contains(&"name".to_string()));
assert!(pairs.contains(&"age".to_string()));
}
#[test]
fn test_walk_tagged() {
use crate::Tag;
let tag = Tag::new(0_u64, "datetime");
let content = CBOR::from("2023-01-01T00:00:00Z");
let cbor = CBOR::from(CBORCase::Tagged(tag, content));
let count = RefCell::new(0);
let edges = RefCell::new(Vec::new());
let visitor = |_element: &WalkElement,
_level: usize,
edge: EdgeType,
state: ()|
-> ((), bool) {
*count.borrow_mut() += 1;
edges.borrow_mut().push(edge);
(state, false)
};
cbor.walk((), &visitor);
assert_eq!(*count.borrow(), 2);
let edges = edges.borrow();
assert_eq!(edges[0], EdgeType::None); assert_eq!(edges[1], EdgeType::TaggedContent); }
#[test]
fn test_walk_nested_structure() {
let mut map = Map::new();
map.insert("numbers", vec![1, 2, 3]);
map.insert("text", "hello");
let cbor = CBOR::from(map);
let count = RefCell::new(0);
let visitor = |_element: &WalkElement,
_level: usize,
_edge: EdgeType,
state: ()|
-> ((), bool) {
*count.borrow_mut() += 1;
(state, false)
};
cbor.walk((), &visitor);
assert_eq!(*count.borrow(), 10);
}
#[test]
fn test_walk_early_termination() {
let mut map = Map::new();
map.insert("should_visit", "yes");
map.insert("stop_here", "stop");
map.insert("should_not_visit", vec![1, 2, 3]); let cbor = CBOR::from(map);
let visited = RefCell::new(Vec::new());
let visitor = |element: &WalkElement,
_level: usize,
_edge: EdgeType,
state: ()|
-> ((), bool) {
visited.borrow_mut().push(element.diagnostic_flat());
let should_stop = if let Some(single) = element.as_single() {
matches!(single.as_case(), CBORCase::Text(s) if s == "stop")
} else {
false
};
(state, should_stop)
};
cbor.walk((), &visitor);
let visited = visited.borrow();
assert!(visited.iter().any(|s| s.contains("stop")));
assert!(visited.len() > 1); }
#[test]
fn test_walk_with_state() {
let cbor = CBOR::from(vec![1, 2, 3]);
#[derive(Clone)]
struct WalkState {
depth_sum: i32,
}
let final_state = RefCell::new(WalkState { depth_sum: 0 });
let visitor = |_element: &WalkElement,
level: usize,
_edge: EdgeType,
mut state: WalkState|
-> (WalkState, bool) {
state.depth_sum += level as i32;
*final_state.borrow_mut() = state.clone();
(state, false)
};
cbor.walk(WalkState { depth_sum: 0 }, &visitor);
assert!(final_state.borrow().depth_sum > 0);
}
#[test]
fn test_edge_type_labels() {
assert_eq!(EdgeType::None.label(), None);
assert_eq!(
EdgeType::ArrayElement(5).label(),
Some("arr[5]".to_string())
);
assert_eq!(EdgeType::MapKey.label(), Some("key".to_string()));
assert_eq!(EdgeType::MapValue.label(), Some("val".to_string()));
assert_eq!(
EdgeType::TaggedContent.label(),
Some("content".to_string())
);
}
}