use std::fmt;
use crate::Condition;
use crate::FilterExpressionBuilder;
use crate::FilterExpressionView;
use crate::FilterLimitKind;
use crate::FilterLimits;
use crate::FilterMatchOptions;
use crate::Metadata;
use crate::MetadataError;
use crate::MetadataResult;
use crate::filter::internal::FilterExpressionNode;
use crate::filter::internal::MatchOutcome;
#[derive(Clone, PartialEq)]
#[must_use]
pub struct FilterExpression {
node: FilterExpressionNode,
node_count: usize,
max_depth: usize,
}
impl fmt::Debug for FilterExpression {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("FilterExpression")
.field("node", &self.node)
.finish()
}
}
impl FilterExpression {
#[inline(always)]
#[must_use]
pub const fn builder() -> FilterExpressionBuilder {
FilterExpressionBuilder::new()
}
#[inline(always)]
#[must_use = "the constructed all-matching expression should be used"]
pub const fn match_all() -> Self {
Self::true_expression()
}
#[inline(always)]
#[must_use = "the constructed no-match expression should be used"]
pub const fn match_none() -> Self {
Self::false_expression()
}
#[inline]
pub fn try_and(self, other: Self) -> MetadataResult<Self> {
let expression = Self::and_unchecked(self, other);
expression.validate_limits(FilterLimits::MAX)?;
Ok(expression)
}
#[inline]
pub fn try_or(self, other: Self) -> MetadataResult<Self> {
let expression = Self::or_unchecked(self, other);
expression.validate_limits(FilterLimits::MAX)?;
Ok(expression)
}
#[inline]
pub fn try_not(self) -> MetadataResult<Self> {
let expression = self.negated_unchecked();
expression.validate_limits(FilterLimits::MAX)?;
Ok(expression)
}
#[inline(always)]
#[must_use = "the expression view should be inspected"]
pub fn view(&self) -> FilterExpressionView<'_> {
match &self.node {
FilterExpressionNode::Condition(condition) => FilterExpressionView::Condition(condition),
FilterExpressionNode::And(children) => FilterExpressionView::And(children),
FilterExpressionNode::Or(children) => FilterExpressionView::Or(children),
FilterExpressionNode::Not(inner) => FilterExpressionView::Not(inner),
FilterExpressionNode::True => FilterExpressionView::True,
FilterExpressionNode::False => FilterExpressionView::False,
}
}
#[inline]
pub(crate) fn condition(condition: Condition) -> MetadataResult<Self> {
condition.validate_operands()?;
Ok(Self {
node: FilterExpressionNode::Condition(condition),
node_count: 1,
max_depth: 1,
})
}
#[inline]
pub(crate) const fn true_expression() -> Self {
Self {
node: FilterExpressionNode::True,
node_count: 1,
max_depth: 1,
}
}
#[inline]
pub(crate) const fn false_expression() -> Self {
Self {
node: FilterExpressionNode::False,
node_count: 1,
max_depth: 1,
}
}
pub(crate) fn and_unchecked(left: Self, right: Self) -> Self {
if left.is_false() || right.is_false() {
return Self::false_expression();
}
if left.is_true() {
return right;
}
if right.is_true() {
return left;
}
Self::combine_and(left, right)
}
pub(crate) fn or_unchecked(left: Self, right: Self) -> Self {
if left.is_true() || right.is_true() {
return Self::true_expression();
}
if left.is_false() {
return right;
}
if right.is_false() {
return left;
}
Self::combine_or(left, right)
}
#[inline]
pub(crate) fn not_expression(expression: Self) -> Self {
let node_count = expression.node_count + 1;
let max_depth = expression.max_depth + 1;
Self {
node: FilterExpressionNode::Not(Box::new(expression)),
node_count,
max_depth,
}
}
pub(crate) fn negated_unchecked(self) -> Self {
match self {
Self {
node: FilterExpressionNode::True,
..
} => Self::false_expression(),
Self {
node: FilterExpressionNode::False,
..
} => Self::true_expression(),
Self {
node: FilterExpressionNode::Not(inner),
..
} => *inner,
expression => Self::not_expression(expression),
}
}
#[inline]
pub(crate) const fn is_true(&self) -> bool {
matches!(&self.node, FilterExpressionNode::True)
}
#[inline]
pub(crate) const fn is_false(&self) -> bool {
matches!(&self.node, FilterExpressionNode::False)
}
pub(crate) fn evaluate(&self, metadata: &Metadata, options: FilterMatchOptions) -> MatchOutcome {
match &self.node {
FilterExpressionNode::Condition(condition) => {
condition.evaluate(metadata, options.numeric_comparison_policy())
}
FilterExpressionNode::And(children) => {
MatchOutcome::and(children.iter().map(|child| child.evaluate(metadata, options)))
}
FilterExpressionNode::Or(children) => {
MatchOutcome::or(children.iter().map(|child| child.evaluate(metadata, options)))
}
FilterExpressionNode::Not(inner) => inner.evaluate(metadata, options).not(),
FilterExpressionNode::True => MatchOutcome::True,
FilterExpressionNode::False => MatchOutcome::False,
}
}
#[cfg(feature = "schema")]
pub(crate) fn visit_conditions<F>(&self, visitor: &mut F) -> MetadataResult<()>
where
F: FnMut(&Condition) -> MetadataResult<()>,
{
match &self.node {
FilterExpressionNode::Condition(condition) => visitor(condition),
FilterExpressionNode::And(children) | FilterExpressionNode::Or(children) => {
for child in children {
child.visit_conditions(visitor)?;
}
Ok(())
}
FilterExpressionNode::Not(inner) => inner.visit_conditions(visitor),
FilterExpressionNode::True | FilterExpressionNode::False => Ok(()),
}
}
pub(crate) fn validate_limits(&self, limits: FilterLimits) -> MetadataResult<()> {
let mut node_count = 0;
self.validate_limits_at(limits, 1, &mut node_count)
}
pub(crate) fn validate_structure_limits(&self, limits: FilterLimits) -> MetadataResult<()> {
if self.max_depth > limits.max_depth() {
return Err(MetadataError::FilterLimitExceeded {
kind: FilterLimitKind::Depth,
value: limits.max_depth() + 1,
maximum: limits.max_depth(),
});
}
if self.node_count > limits.max_nodes() {
return Err(MetadataError::FilterLimitExceeded {
kind: FilterLimitKind::Nodes,
value: limits.max_nodes() + 1,
maximum: limits.max_nodes(),
});
}
Ok(())
}
fn validate_limits_at(&self, limits: FilterLimits, depth: usize, node_count: &mut usize) -> MetadataResult<()> {
if depth > limits.max_depth() {
return Err(MetadataError::FilterLimitExceeded {
kind: FilterLimitKind::Depth,
value: depth,
maximum: limits.max_depth(),
});
}
*node_count += 1;
if *node_count > limits.max_nodes() {
return Err(MetadataError::FilterLimitExceeded {
kind: FilterLimitKind::Nodes,
value: *node_count,
maximum: limits.max_nodes(),
});
}
match &self.node {
FilterExpressionNode::Condition(condition) => condition.validate_limits(limits),
FilterExpressionNode::And(children) | FilterExpressionNode::Or(children) => {
for child in children {
child.validate_limits_at(limits, depth + 1, node_count)?;
}
Ok(())
}
FilterExpressionNode::Not(inner) => inner.validate_limits_at(limits, depth + 1, node_count),
FilterExpressionNode::True | FilterExpressionNode::False => Ok(()),
}
}
#[cfg(test)]
fn assert_cached_metrics_consistent(&self) {
match &self.node {
FilterExpressionNode::And(children) | FilterExpressionNode::Or(children) => {
for child in children {
child.assert_cached_metrics_consistent();
}
}
FilterExpressionNode::Not(inner) => inner.assert_cached_metrics_consistent(),
FilterExpressionNode::Condition(_) | FilterExpressionNode::True | FilterExpressionNode::False => {}
}
let (node_count, max_depth) = self.recursive_metrics();
assert_eq!(
self.node_count, node_count,
"cached node count differs from the expression tree"
);
assert_eq!(
self.max_depth, max_depth,
"cached maximum depth differs from the expression tree"
);
}
#[cfg(test)]
fn recursive_metrics(&self) -> (usize, usize) {
match &self.node {
FilterExpressionNode::Condition(_) | FilterExpressionNode::True | FilterExpressionNode::False => (1, 1),
FilterExpressionNode::And(children) | FilterExpressionNode::Or(children) => {
let mut node_count = 1;
let mut max_child_depth = 0;
for child in children {
let (child_node_count, child_max_depth) = child.recursive_metrics();
node_count += child_node_count;
max_child_depth = max_child_depth.max(child_max_depth);
}
(node_count, max_child_depth + 1)
}
FilterExpressionNode::Not(inner) => {
let (node_count, max_depth) = inner.recursive_metrics();
(node_count + 1, max_depth + 1)
}
}
}
fn combine_and(left: Self, right: Self) -> Self {
let left_same_kind = matches!(&left.node, FilterExpressionNode::And(_));
let right_same_kind = matches!(&right.node, FilterExpressionNode::And(_));
let (node_count, max_depth) = Self::combined_metrics(&left, &right, left_same_kind, right_same_kind);
let mut children = match left {
Self {
node: FilterExpressionNode::And(children),
..
} => children,
expression => vec![expression],
};
match right {
Self {
node: FilterExpressionNode::And(mut nested),
..
} => children.append(&mut nested),
expression => children.push(expression),
}
Self {
node: FilterExpressionNode::And(children),
node_count,
max_depth,
}
}
fn combine_or(left: Self, right: Self) -> Self {
let left_same_kind = matches!(&left.node, FilterExpressionNode::Or(_));
let right_same_kind = matches!(&right.node, FilterExpressionNode::Or(_));
let (node_count, max_depth) = Self::combined_metrics(&left, &right, left_same_kind, right_same_kind);
let mut children = match left {
Self {
node: FilterExpressionNode::Or(children),
..
} => children,
expression => vec![expression],
};
match right {
Self {
node: FilterExpressionNode::Or(mut nested),
..
} => children.append(&mut nested),
expression => children.push(expression),
}
Self {
node: FilterExpressionNode::Or(children),
node_count,
max_depth,
}
}
fn combined_metrics(left: &Self, right: &Self, left_same_kind: bool, right_same_kind: bool) -> (usize, usize) {
let node_count = match (left_same_kind, right_same_kind) {
(true, true) => left.node_count + right.node_count - 1,
(true, false) | (false, true) => left.node_count + right.node_count,
(false, false) => left.node_count + right.node_count + 1,
};
let max_depth = match (left_same_kind, right_same_kind) {
(true, true) => left.max_depth.max(right.max_depth),
(true, false) => left.max_depth.max(right.max_depth + 1),
(false, true) => (left.max_depth + 1).max(right.max_depth),
(false, false) => left.max_depth.max(right.max_depth) + 1,
};
(node_count, max_depth)
}
}
#[cfg(test)]
mod tests {
use super::FilterExpression;
#[test]
fn test_cached_metrics_match_recursive_metrics() {
let leaf = FilterExpression::builder()
.exists("leaf")
.build()
.expect("leaf expression should build");
leaf.assert_cached_metrics_consistent();
let left = FilterExpression::builder()
.exists("left_1")
.exists("left_2")
.build()
.expect("left expression should build");
let right = FilterExpression::builder()
.exists("right_1")
.exists("right_2")
.build()
.expect("right expression should build");
let flattened = left.try_and(right).expect("flattened AND should build");
flattened.assert_cached_metrics_consistent();
let nested = flattened
.try_or(
FilterExpression::builder()
.exists("alternative")
.build()
.expect("alternative expression should build"),
)
.expect("nested OR should build")
.try_not()
.expect("negated expression should build");
nested.assert_cached_metrics_consistent();
FilterExpression::match_all()
.try_and(nested.clone())
.expect("true AND expression should simplify")
.assert_cached_metrics_consistent();
FilterExpression::match_none()
.try_or(nested)
.expect("false OR expression should simplify")
.assert_cached_metrics_consistent();
FilterExpression::match_all()
.try_not()
.expect("constant negation should simplify")
.assert_cached_metrics_consistent();
}
}