use crate::{Bitmap, FieldName, FieldValuePair, FileIndex};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "allocative", derive(allocative::Allocative))]
enum FilterTarget {
Field(FieldName),
Pair(FieldValuePair),
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "allocative", derive(allocative::Allocative))]
pub struct Filter {
inner: Arc<FilterExpr<FilterTarget>>,
}
impl Filter {
pub fn match_field_name(name: FieldName) -> Self {
Self {
inner: Arc::new(FilterExpr::Match(FilterTarget::Field(name))),
}
}
pub fn match_field_value_pair(pair: FieldValuePair) -> Self {
Self {
inner: Arc::new(FilterExpr::Match(FilterTarget::Pair(pair))),
}
}
pub fn and(filters: Vec<Self>) -> Self {
let inner_filters: Vec<FilterExpr<FilterTarget>> =
filters.into_iter().map(|f| (*f.inner).clone()).collect();
Self {
inner: Arc::new(FilterExpr::and(inner_filters)),
}
}
pub fn or(filters: Vec<Self>) -> Self {
let inner_filters: Vec<FilterExpr<FilterTarget>> =
filters.into_iter().map(|f| (*f.inner).clone()).collect();
Self {
inner: Arc::new(FilterExpr::or(inner_filters)),
}
}
pub fn none() -> Self {
Self {
inner: Arc::new(FilterExpr::None),
}
}
pub fn is_none(&self) -> bool {
matches!(self.inner.as_ref(), FilterExpr::None)
}
pub fn evaluate(&self, file_index: &FileIndex) -> Bitmap {
self.inner.resolve(file_index).evaluate()
}
}
impl PartialEq for Filter {
fn eq(&self, other: &Self) -> bool {
if Arc::ptr_eq(&self.inner, &other.inner) {
return true;
}
self.inner == other.inner
}
}
impl Eq for Filter {}
impl std::hash::Hash for Filter {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.inner.hash(state);
}
}
impl std::fmt::Display for Filter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner)
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "allocative", derive(allocative::Allocative))]
enum FilterExpr<T> {
None,
Match(T),
Conjunction(Vec<Self>),
Disjunction(Vec<Self>),
}
impl Eq for FilterExpr<FilterTarget> {}
impl Hash for FilterExpr<FilterTarget> {
fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
FilterExpr::None => {}
FilterExpr::Match(target) => target.hash(state),
FilterExpr::Conjunction(filters) => filters.hash(state),
FilterExpr::Disjunction(filters) => filters.hash(state),
}
}
}
impl FilterExpr<FilterTarget> {
fn and(filters: Vec<Self>) -> Self {
let mut flattened = Vec::new();
for filter in filters {
match filter {
FilterExpr::Conjunction(inner) => flattened.extend(inner),
FilterExpr::None => continue,
other => flattened.push(other),
}
}
match flattened.len() {
0 => FilterExpr::None,
1 => flattened.into_iter().next().unwrap(),
_ => FilterExpr::Conjunction(flattened),
}
}
fn or(filters: Vec<Self>) -> Self {
let mut flattened = Vec::new();
for filter in filters {
match filter {
FilterExpr::Disjunction(inner) => flattened.extend(inner),
FilterExpr::None => continue,
other => flattened.push(other),
}
}
match flattened.len() {
0 => FilterExpr::None,
1 => flattened.into_iter().next().unwrap(),
_ => FilterExpr::Disjunction(flattened),
}
}
fn resolve(&self, file_index: &FileIndex) -> FilterExpr<Bitmap> {
match self {
FilterExpr::None => FilterExpr::None,
FilterExpr::Match(target) => match target {
FilterTarget::Field(field_name) => {
let matches: Vec<_> = file_index
.bitmaps()
.iter()
.filter(|(pair, _)| pair.field() == field_name.as_str())
.map(|(_, bitmap)| FilterExpr::Match(bitmap.clone()))
.collect();
match matches.len() {
0 => FilterExpr::None,
1 => matches.into_iter().next().unwrap(),
_ => FilterExpr::Disjunction(matches),
}
}
FilterTarget::Pair(pair) => {
if let Some(bitmap) = file_index.bitmaps().get(pair) {
FilterExpr::Match(bitmap.clone())
} else {
FilterExpr::None
}
}
},
FilterExpr::Conjunction(filters) => {
let mut resolved = Vec::with_capacity(filters.len());
for filter in filters {
let r = filter.resolve(file_index);
if matches!(r, FilterExpr::None) {
return FilterExpr::None;
}
resolved.push(r);
}
match resolved.len() {
0 => FilterExpr::None,
1 => resolved.into_iter().next().unwrap(),
_ => FilterExpr::Conjunction(resolved),
}
}
FilterExpr::Disjunction(filters) => {
let mut resolved = Vec::with_capacity(filters.len());
for filter in filters {
let r = filter.resolve(file_index);
if !matches!(r, FilterExpr::None) {
resolved.push(r);
}
}
match resolved.len() {
0 => FilterExpr::None,
1 => resolved.into_iter().next().unwrap(),
_ => FilterExpr::Disjunction(resolved),
}
}
}
}
}
impl std::fmt::Display for FilterExpr<FilterTarget> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FilterExpr::None => write!(f, "None"),
FilterExpr::Match(target) => write_filter_target(f, target),
FilterExpr::Conjunction(filters) => write_filter_list(f, filters, " AND "),
FilterExpr::Disjunction(filters) => write_filter_list(f, filters, " OR "),
}
}
}
fn write_filter_target(f: &mut std::fmt::Formatter<'_>, target: &FilterTarget) -> std::fmt::Result {
match target {
FilterTarget::Field(name) => write!(f, "{}", name),
FilterTarget::Pair(pair) => write!(f, "{}", pair),
}
}
fn write_filter_list(
f: &mut std::fmt::Formatter<'_>,
filters: &[FilterExpr<FilterTarget>],
separator: &str,
) -> std::fmt::Result {
write!(f, "(")?;
for (i, filter) in filters.iter().enumerate() {
if i > 0 {
write!(f, "{separator}")?;
}
write!(f, "{}", filter)?;
}
write!(f, ")")
}
impl FilterExpr<Bitmap> {
fn evaluate(&self) -> Bitmap {
match self {
Self::None => Bitmap::new(),
Self::Match(bitmap) => bitmap.clone(),
Self::Conjunction(filter_exprs) => {
if filter_exprs.is_empty() {
return Bitmap::new();
}
let mut result = filter_exprs[0].evaluate();
for expr in filter_exprs.iter().skip(1) {
result &= expr.evaluate();
if result.is_empty() {
break; }
}
result
}
Self::Disjunction(filter_exprs) => {
let mut result = Bitmap::new();
for expr in filter_exprs.iter() {
result |= expr.evaluate();
}
result
}
}
}
}