use super::{Predicate, PredicateNode};
#[derive(Clone, Debug, Default)]
pub struct VisitOutcome<T> {
pub value: Option<T>,
pub residual: Option<Predicate>,
}
impl<T> VisitOutcome<T> {
pub fn value(value: T) -> Self {
Self {
value: Some(value),
residual: None,
}
}
pub fn residual(residual: Predicate) -> Self {
Self {
value: None,
residual: Some(residual),
}
}
pub fn empty() -> Self {
Self {
value: None,
residual: None,
}
}
}
pub trait PredicateVisitor {
type Error;
type Value;
fn visit_leaf(
&mut self,
leaf: &PredicateNode,
) -> Result<VisitOutcome<Self::Value>, Self::Error>;
fn combine_not(
&mut self,
original: &Predicate,
child: VisitOutcome<Self::Value>,
) -> Result<VisitOutcome<Self::Value>, Self::Error>;
fn combine_and(
&mut self,
original: &Predicate,
children: Vec<VisitOutcome<Self::Value>>,
) -> Result<VisitOutcome<Self::Value>, Self::Error>;
fn combine_or(
&mut self,
original: &Predicate,
children: Vec<VisitOutcome<Self::Value>>,
) -> Result<VisitOutcome<Self::Value>, Self::Error>;
fn visit_predicate(
&mut self,
predicate: &Predicate,
) -> Result<VisitOutcome<Self::Value>, Self::Error> {
self.visit_node(predicate.kind(), predicate)
}
fn visit_node(
&mut self,
node: &PredicateNode,
original: &Predicate,
) -> Result<VisitOutcome<Self::Value>, Self::Error> {
match node {
PredicateNode::Not(inner) => {
let child = self.visit_predicate(inner)?;
self.combine_not(original, child)
}
PredicateNode::And(clauses) => {
debug_assert!(
!clauses.is_empty(),
"Predicate::make_and enforces at least one clause"
);
let mut children = Vec::with_capacity(clauses.len());
for clause in clauses {
children.push(self.visit_predicate(clause)?);
}
self.combine_and(original, children)
}
PredicateNode::Or(clauses) => {
debug_assert!(
!clauses.is_empty(),
"Predicate::make_or enforces at least one clause"
);
let mut children = Vec::with_capacity(clauses.len());
for clause in clauses {
children.push(self.visit_predicate(clause)?);
}
self.combine_or(original, children)
}
leaf => {
debug_assert!(leaf.is_leaf(), "non-leaf nodes handled earlier");
self.visit_leaf(leaf)
}
}
}
}