Skip to main content

mail_query/
visitor.rs

1//! Visitor pattern for walking a parsed AST.
2//!
3//! Implementations override the leaf hooks they care about and let the
4//! default [`Visitor::visit_node`] handle tree traversal. The pattern
5//! mirrors `syn` and `sqlparser-rs`.
6//!
7//! ```
8//! use mail_query::{parse, FilterKind, QueryNode, Visitor};
9//!
10//! #[derive(Default)]
11//! struct CountFilters(usize);
12//! impl Visitor for CountFilters {
13//!     fn visit_filter(&mut self, _: &FilterKind) {
14//!         self.0 += 1;
15//!     }
16//! }
17//!
18//! let ast = parse("from:alice is:unread has:attachment").expect("parses");
19//! let mut counter = CountFilters::default();
20//! counter.walk(&ast);
21//! assert_eq!(counter.0, 2);
22//! ```
23
24use crate::ast::{DateBound, DateValue, FilterKind, QueryField, QueryNode, SizeOp};
25
26/// AST visitor.
27///
28/// Implementors override the per-variant hooks they care about. Call
29/// [`Visitor::walk`] on a root [`QueryNode`] to start traversal —
30/// `walk` recurses into compound nodes (`And`/`Or`/`Not`) and dispatches
31/// to leaf hooks.
32#[allow(unused_variables)]
33pub trait Visitor {
34    /// Recursive walk. Visits leaves via the typed hooks below;
35    /// recurses into `And`/`Or`/`Not` children. Override only if you
36    /// need to short-circuit traversal.
37    fn walk(&mut self, node: &QueryNode) {
38        match node {
39            QueryNode::Text(s) => self.visit_text(s),
40            QueryNode::Exact(s) => self.visit_exact(s),
41            QueryNode::Phrase(s) => self.visit_phrase(s),
42            QueryNode::Field { field, value } => self.visit_field(*field, value),
43            QueryNode::Filter(kind) => self.visit_filter(kind),
44            QueryNode::Label(name) => self.visit_label(name),
45            QueryNode::DateRange { bound, date } => self.visit_date(*bound, date),
46            QueryNode::Size { op, bytes } => self.visit_size(*op, *bytes),
47            QueryNode::Near {
48                left,
49                right,
50                distance,
51            } => self.visit_near(left, right, *distance),
52            QueryNode::And(l, r) => {
53                self.visit_and_pre(l, r);
54                self.walk(l);
55                self.walk(r);
56                self.visit_and_post(l, r);
57            }
58            QueryNode::Or(l, r) => {
59                self.visit_or_pre(l, r);
60                self.walk(l);
61                self.walk(r);
62                self.visit_or_post(l, r);
63            }
64            QueryNode::Not(inner) => {
65                self.visit_not_pre(inner);
66                self.walk(inner);
67                self.visit_not_post(inner);
68            }
69        }
70    }
71
72    fn visit_text(&mut self, s: &str) {}
73    fn visit_exact(&mut self, s: &str) {}
74    fn visit_phrase(&mut self, s: &str) {}
75    fn visit_field(&mut self, field: QueryField, value: &str) {}
76    fn visit_filter(&mut self, kind: &FilterKind) {}
77    fn visit_label(&mut self, name: &str) {}
78    fn visit_date(&mut self, bound: DateBound, date: &DateValue) {}
79    fn visit_size(&mut self, op: SizeOp, bytes: u64) {}
80    fn visit_near(&mut self, left: &str, right: &str, distance: u32) {}
81
82    fn visit_and_pre(&mut self, left: &QueryNode, right: &QueryNode) {}
83    fn visit_and_post(&mut self, left: &QueryNode, right: &QueryNode) {}
84    fn visit_or_pre(&mut self, left: &QueryNode, right: &QueryNode) {}
85    fn visit_or_post(&mut self, left: &QueryNode, right: &QueryNode) {}
86    fn visit_not_pre(&mut self, inner: &QueryNode) {}
87    fn visit_not_post(&mut self, inner: &QueryNode) {}
88}