use serde::{Deserialize, Serialize};
use crate::{
Condition, FilterMatchOptions, Metadata, MetadataFilterBuilder, MetadataResult,
MissingKeyPolicy, NumberComparisonPolicy,
};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub(crate) enum FilterExpr {
Condition(Condition),
And(Vec<FilterExpr>),
Or(Vec<FilterExpr>),
Not(Box<FilterExpr>),
False,
}
impl FilterExpr {
#[inline]
fn append_and_child(children: &mut Vec<FilterExpr>, expr: FilterExpr) {
match expr {
FilterExpr::And(mut nested) => children.append(&mut nested),
other => children.push(other),
}
}
#[inline]
fn append_or_child(children: &mut Vec<FilterExpr>, expr: FilterExpr) {
match expr {
FilterExpr::Or(mut nested) => children.append(&mut nested),
other => children.push(other),
}
}
#[inline]
pub(crate) fn and(lhs: FilterExpr, rhs: FilterExpr) -> FilterExpr {
if matches!(lhs, FilterExpr::False) || matches!(rhs, FilterExpr::False) {
return FilterExpr::False;
}
let mut children = Vec::new();
Self::append_and_child(&mut children, lhs);
Self::append_and_child(&mut children, rhs);
FilterExpr::And(children)
}
#[inline]
pub(crate) fn or(lhs: FilterExpr, rhs: FilterExpr) -> FilterExpr {
if matches!(lhs, FilterExpr::False) {
return rhs;
}
if matches!(rhs, FilterExpr::False) {
return lhs;
}
let mut children = Vec::new();
Self::append_or_child(&mut children, lhs);
Self::append_or_child(&mut children, rhs);
FilterExpr::Or(children)
}
#[inline]
fn matches(&self, meta: &Metadata, options: FilterMatchOptions) -> bool {
match self {
FilterExpr::Condition(condition) => condition.matches(
meta,
options.missing_key_policy,
options.number_comparison_policy,
),
FilterExpr::And(children) => children.iter().all(|expr| expr.matches(meta, options)),
FilterExpr::Or(children) => children.iter().any(|expr| expr.matches(meta, options)),
FilterExpr::Not(inner) => !inner.matches(meta, options),
FilterExpr::False => false,
}
}
fn visit_conditions<F>(&self, visitor: &mut F) -> MetadataResult<()>
where
F: FnMut(&Condition) -> MetadataResult<()>,
{
match self {
FilterExpr::Condition(condition) => visitor(condition),
FilterExpr::And(children) | FilterExpr::Or(children) => {
for child in children {
child.visit_conditions(visitor)?;
}
Ok(())
}
FilterExpr::Not(inner) => inner.visit_conditions(visitor),
FilterExpr::False => Ok(()),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct MetadataFilter {
#[serde(default, skip_serializing_if = "Option::is_none")]
expr: Option<FilterExpr>,
#[serde(default)]
options: FilterMatchOptions,
}
impl MetadataFilter {
#[inline]
pub(crate) fn new(expr: Option<FilterExpr>, options: FilterMatchOptions) -> Self {
Self { expr, options }
}
#[inline]
#[must_use]
pub fn builder() -> MetadataFilterBuilder {
MetadataFilterBuilder::default()
}
#[inline]
#[must_use]
pub fn all() -> Self {
Self::default()
}
#[inline]
#[must_use]
pub fn none() -> Self {
Self {
expr: Some(FilterExpr::False),
options: FilterMatchOptions::default(),
}
}
#[inline]
#[must_use]
pub fn options(&self) -> FilterMatchOptions {
self.options
}
#[inline]
#[must_use]
pub fn with_options(mut self, options: FilterMatchOptions) -> Self {
self.options = options;
self
}
#[inline]
#[must_use]
pub fn with_missing_key_policy(mut self, missing_key_policy: MissingKeyPolicy) -> Self {
self.options.missing_key_policy = missing_key_policy;
self
}
#[inline]
#[must_use]
pub fn with_number_comparison_policy(
mut self,
number_comparison_policy: NumberComparisonPolicy,
) -> Self {
self.options.number_comparison_policy = number_comparison_policy;
self
}
#[allow(clippy::should_implement_trait)]
#[inline]
#[must_use]
pub fn not(mut self) -> Self {
self.expr = MetadataFilterBuilder::negate_expr(self.expr);
self
}
#[inline]
#[must_use]
pub fn matches(&self, meta: &Metadata) -> bool {
self.matches_with_options(meta, self.options)
}
#[inline]
#[must_use]
pub fn matches_with_options(&self, meta: &Metadata, options: FilterMatchOptions) -> bool {
self.expr
.as_ref()
.is_none_or(|expr| expr.matches(meta, options))
}
pub(crate) fn visit_conditions<F>(&self, mut visitor: F) -> MetadataResult<()>
where
F: FnMut(&Condition) -> MetadataResult<()>,
{
if let Some(expr) = &self.expr {
expr.visit_conditions(&mut visitor)?;
}
Ok(())
}
}
impl std::ops::Not for MetadataFilter {
type Output = MetadataFilter;
#[inline]
fn not(self) -> Self::Output {
MetadataFilter::not(self)
}
}