use crate::visitor::{VisitDecision, VisitorContext};
use crate::{Document, MatrixList, Node, Value};
pub trait Visitor {
fn begin_document(&mut self, _doc: &Document, _ctx: &VisitorContext<'_>) -> VisitDecision {
VisitDecision::Continue
}
fn end_document(&mut self, _doc: &Document, _ctx: &VisitorContext<'_>) -> VisitDecision {
VisitDecision::Continue
}
fn visit_scalar(
&mut self,
_key: &str,
_value: &Value,
_ctx: &VisitorContext<'_>,
) -> VisitDecision {
VisitDecision::Continue
}
fn begin_object(&mut self, _key: &str, _ctx: &VisitorContext<'_>) -> VisitDecision {
VisitDecision::Continue
}
fn end_object(&mut self, _key: &str, _ctx: &VisitorContext<'_>) -> VisitDecision {
VisitDecision::Continue
}
fn begin_list(
&mut self,
_key: &str,
_list: &MatrixList,
_ctx: &VisitorContext<'_>,
) -> VisitDecision {
VisitDecision::Continue
}
fn end_list(
&mut self,
_key: &str,
_list: &MatrixList,
_ctx: &VisitorContext<'_>,
) -> VisitDecision {
VisitDecision::Continue
}
fn visit_node(&mut self, _node: &Node, _ctx: &VisitorContext<'_>) -> VisitDecision {
VisitDecision::Continue
}
fn begin_node_children(&mut self, _node: &Node, _ctx: &VisitorContext<'_>) -> VisitDecision {
VisitDecision::Continue
}
fn end_node_children(&mut self, _node: &Node, _ctx: &VisitorContext<'_>) -> VisitDecision {
VisitDecision::Continue
}
fn visit_reference(
&mut self,
_reference: &crate::Reference,
_ctx: &VisitorContext<'_>,
) -> VisitDecision {
VisitDecision::Continue
}
fn visit_expression(
&mut self,
_expr: &crate::lex::Expression,
_ctx: &VisitorContext<'_>,
) -> VisitDecision {
VisitDecision::Continue
}
fn visit_tensor(
&mut self,
_tensor: &crate::lex::Tensor,
_ctx: &VisitorContext<'_>,
) -> VisitDecision {
VisitDecision::Continue
}
}
#[cfg(test)]
mod tests {
use super::*;
struct NoOpVisitor;
impl Visitor for NoOpVisitor {}
#[test]
fn test_default_implementations_return_continue() {
let mut visitor = NoOpVisitor;
let doc = Document::new((2, 0));
let ctx = VisitorContext::new(&doc);
assert_eq!(visitor.begin_document(&doc, &ctx), VisitDecision::Continue);
assert_eq!(visitor.end_document(&doc, &ctx), VisitDecision::Continue);
assert_eq!(
visitor.visit_scalar("key", &Value::Null, &ctx),
VisitDecision::Continue
);
assert_eq!(visitor.begin_object("key", &ctx), VisitDecision::Continue);
assert_eq!(visitor.end_object("key", &ctx), VisitDecision::Continue);
}
struct CountingVisitor {
scalar_count: usize,
node_count: usize,
}
impl Visitor for CountingVisitor {
fn visit_scalar(&mut self, _: &str, _: &Value, _: &VisitorContext<'_>) -> VisitDecision {
self.scalar_count += 1;
VisitDecision::Continue
}
fn visit_node(&mut self, _: &Node, _: &VisitorContext<'_>) -> VisitDecision {
self.node_count += 1;
VisitDecision::Continue
}
}
#[test]
fn test_visitor_can_count_elements() {
let mut visitor = CountingVisitor {
scalar_count: 0,
node_count: 0,
};
let doc = Document::new((2, 0));
let ctx = VisitorContext::new(&doc);
visitor.visit_scalar("key", &Value::Int(42), &ctx);
visitor.visit_scalar("key2", &Value::String("test".into()), &ctx);
let node = Node::new("User", "1", vec![]);
visitor.visit_node(&node, &ctx);
assert_eq!(visitor.scalar_count, 2);
assert_eq!(visitor.node_count, 1);
}
struct EarlyStopVisitor {
stop_after: usize,
count: usize,
}
impl Visitor for EarlyStopVisitor {
fn visit_node(&mut self, _: &Node, _: &VisitorContext<'_>) -> VisitDecision {
self.count += 1;
if self.count >= self.stop_after {
VisitDecision::Stop
} else {
VisitDecision::Continue
}
}
}
#[test]
fn test_visitor_can_stop_early() {
let mut visitor = EarlyStopVisitor {
stop_after: 2,
count: 0,
};
let doc = Document::new((2, 0));
let ctx = VisitorContext::new(&doc);
let node = Node::new("User", "1", vec![]);
assert_eq!(visitor.visit_node(&node, &ctx), VisitDecision::Continue);
assert_eq!(visitor.visit_node(&node, &ctx), VisitDecision::Stop);
assert_eq!(visitor.count, 2);
}
struct SkipChildrenVisitor;
impl Visitor for SkipChildrenVisitor {
fn begin_object(&mut self, _: &str, _: &VisitorContext<'_>) -> VisitDecision {
VisitDecision::SkipChildren
}
}
#[test]
fn test_visitor_can_skip_children() {
let mut visitor = SkipChildrenVisitor;
let doc = Document::new((2, 0));
let ctx = VisitorContext::new(&doc);
assert_eq!(
visitor.begin_object("obj", &ctx),
VisitDecision::SkipChildren
);
}
}