Skip to main content

tonbo_predicate/core/
visitor.rs

1use super::{Predicate, PredicateNode};
2
3/// Result produced while evaluating parts of a predicate tree.
4#[derive(Clone, Debug, Default)]
5pub struct VisitOutcome<T> {
6    /// Computed value for the evaluated portion, when available.
7    pub value: Option<T>,
8    /// Residual predicate that still needs evaluation elsewhere.
9    pub residual: Option<Predicate>,
10}
11
12impl<T> VisitOutcome<T> {
13    /// Outcome containing only a computed value.
14    pub fn value(value: T) -> Self {
15        Self {
16            value: Some(value),
17            residual: None,
18        }
19    }
20
21    /// Outcome containing only a residual predicate.
22    pub fn residual(residual: Predicate) -> Self {
23        Self {
24            value: None,
25            residual: Some(residual),
26        }
27    }
28
29    /// Outcome without value or residual.
30    pub fn empty() -> Self {
31        Self {
32            value: None,
33            residual: None,
34        }
35    }
36}
37
38/// Visitor that walks predicate trees and emits custom results plus residual predicates.
39pub trait PredicateVisitor {
40    /// Error type used when evaluation fails.
41    type Error;
42    /// Concrete value type produced while walking the predicate.
43    type Value;
44
45    /// Evaluates a leaf predicate and returns its result.
46    fn visit_leaf(
47        &mut self,
48        leaf: &PredicateNode,
49    ) -> Result<VisitOutcome<Self::Value>, Self::Error>;
50
51    /// Combines the result of a negated child predicate.
52    fn combine_not(
53        &mut self,
54        original: &Predicate,
55        child: VisitOutcome<Self::Value>,
56    ) -> Result<VisitOutcome<Self::Value>, Self::Error>;
57
58    /// Combines an `AND` clause from the supplied child results.
59    fn combine_and(
60        &mut self,
61        original: &Predicate,
62        children: Vec<VisitOutcome<Self::Value>>,
63    ) -> Result<VisitOutcome<Self::Value>, Self::Error>;
64
65    /// Combines an `OR` clause from the supplied child results.
66    fn combine_or(
67        &mut self,
68        original: &Predicate,
69        children: Vec<VisitOutcome<Self::Value>>,
70    ) -> Result<VisitOutcome<Self::Value>, Self::Error>;
71
72    /// Visits the supplied predicate by walking the expression tree.
73    fn visit_predicate(
74        &mut self,
75        predicate: &Predicate,
76    ) -> Result<VisitOutcome<Self::Value>, Self::Error> {
77        self.visit_node(predicate.kind(), predicate)
78    }
79
80    /// Internal helper that evaluates a predicate node recursively.
81    fn visit_node(
82        &mut self,
83        node: &PredicateNode,
84        original: &Predicate,
85    ) -> Result<VisitOutcome<Self::Value>, Self::Error> {
86        match node {
87            PredicateNode::Not(inner) => {
88                let child = self.visit_predicate(inner)?;
89                self.combine_not(original, child)
90            }
91            PredicateNode::And(clauses) => {
92                debug_assert!(
93                    !clauses.is_empty(),
94                    "Predicate::make_and enforces at least one clause"
95                );
96                let mut children = Vec::with_capacity(clauses.len());
97                for clause in clauses {
98                    children.push(self.visit_predicate(clause)?);
99                }
100                self.combine_and(original, children)
101            }
102            PredicateNode::Or(clauses) => {
103                debug_assert!(
104                    !clauses.is_empty(),
105                    "Predicate::make_or enforces at least one clause"
106                );
107                let mut children = Vec::with_capacity(clauses.len());
108                for clause in clauses {
109                    children.push(self.visit_predicate(clause)?);
110                }
111                self.combine_or(original, children)
112            }
113            leaf => {
114                debug_assert!(leaf.is_leaf(), "non-leaf nodes handled earlier");
115                self.visit_leaf(leaf)
116            }
117        }
118    }
119}