use crate::{Operator, Value};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FilterOperator {
Single(Filter),
And(Vec<FilterOperator>),
Or(Vec<FilterOperator>),
Not(Box<FilterOperator>),
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Filter {
pub column: String,
pub operator: Operator,
pub value: FilterValue,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FilterValue {
Single(Value),
Multiple(Vec<Value>),
Range(Value, Value),
}
impl Filter {
pub fn new(column: impl Into<String>, operator: Operator, value: FilterValue) -> Self {
Self {
column: column.into(),
operator,
value,
}
}
pub fn new_simple(
column: impl Into<String>,
operator: Operator,
value: impl Into<Value>,
) -> Self {
Self {
column: column.into(),
operator,
value: FilterValue::Single(value.into()),
}
}
pub fn eq(column: impl Into<String>, value: impl Into<Value>) -> Self {
Self::new(column, Operator::Eq, FilterValue::Single(value.into()))
}
pub fn ne(column: impl Into<String>, value: impl Into<Value>) -> Self {
Self::new(column, Operator::Ne, FilterValue::Single(value.into()))
}
pub fn lt(column: impl Into<String>, value: impl Into<Value>) -> Self {
Self::new(column, Operator::Lt, FilterValue::Single(value.into()))
}
pub fn le(column: impl Into<String>, value: impl Into<Value>) -> Self {
Self::new(column, Operator::Le, FilterValue::Single(value.into()))
}
pub fn gt(column: impl Into<String>, value: impl Into<Value>) -> Self {
Self::new(column, Operator::Gt, FilterValue::Single(value.into()))
}
pub fn ge(column: impl Into<String>, value: impl Into<Value>) -> Self {
Self::new(column, Operator::Ge, FilterValue::Single(value.into()))
}
pub fn like(column: impl Into<String>, pattern: impl Into<String>) -> Self {
Self::new(
column,
Operator::Like,
FilterValue::Single(Value::Text(pattern.into())),
)
}
pub fn not_like(column: impl Into<String>, pattern: impl Into<String>) -> Self {
Self::new(
column,
Operator::NotLike,
FilterValue::Single(Value::Text(pattern.into())),
)
}
pub fn in_values(column: impl Into<String>, values: Vec<impl Into<Value>>) -> Self {
let values = values.into_iter().map(|v| v.into()).collect();
Self::new(column, Operator::In, FilterValue::Multiple(values))
}
pub fn not_in_values(column: impl Into<String>, values: Vec<impl Into<Value>>) -> Self {
let values = values.into_iter().map(|v| v.into()).collect();
Self::new(column, Operator::NotIn, FilterValue::Multiple(values))
}
pub fn is_null(column: impl Into<String>) -> Self {
Self::new(column, Operator::IsNull, FilterValue::Single(Value::Null))
}
pub fn is_not_null(column: impl Into<String>) -> Self {
Self::new(
column,
Operator::IsNotNull,
FilterValue::Single(Value::Null),
)
}
pub fn between(
column: impl Into<String>,
min: impl Into<Value>,
max: impl Into<Value>,
) -> Self {
Self::new(
column,
Operator::Between,
FilterValue::Range(min.into(), max.into()),
)
}
pub fn not_between(
column: impl Into<String>,
min: impl Into<Value>,
max: impl Into<Value>,
) -> Self {
Self::new(
column,
Operator::NotBetween,
FilterValue::Range(min.into(), max.into()),
)
}
}
impl FilterOperator {
pub fn and(filters: Vec<FilterOperator>) -> Self {
FilterOperator::And(filters)
}
pub fn or(filters: Vec<FilterOperator>) -> Self {
FilterOperator::Or(filters)
}
pub fn negate(filter: FilterOperator) -> Self {
FilterOperator::Not(Box::new(filter))
}
pub fn and_with(self, other: FilterOperator) -> Self {
match self {
FilterOperator::And(mut filters) => {
filters.push(other);
FilterOperator::And(filters)
}
_ => FilterOperator::And(vec![self, other]),
}
}
pub fn or_with(self, other: FilterOperator) -> Self {
match self {
FilterOperator::Or(mut filters) => {
filters.push(other);
FilterOperator::Or(filters)
}
_ => FilterOperator::Or(vec![self, other]),
}
}
}
impl std::ops::Not for FilterOperator {
type Output = Self;
fn not(self) -> Self::Output {
FilterOperator::Not(Box::new(self))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchFilter {
pub query: String,
pub columns: Vec<String>,
pub case_sensitive: bool,
pub exact_match: bool,
}
impl SearchFilter {
pub fn new(query: impl Into<String>, columns: Vec<impl Into<String>>) -> Self {
Self {
query: query.into(),
columns: columns.into_iter().map(|c| c.into()).collect(),
case_sensitive: false,
exact_match: false,
}
}
pub fn case_sensitive(mut self, case_sensitive: bool) -> Self {
self.case_sensitive = case_sensitive;
self
}
pub fn exact_match(mut self, exact_match: bool) -> Self {
self.exact_match = exact_match;
self
}
pub fn to_filter_operator(&self) -> FilterOperator {
let mut filters = Vec::new();
for column in &self.columns {
let filter = if self.exact_match {
Filter::eq(column, &*self.query)
} else {
Filter::like(column, format!("%{}%", self.query))
};
filters.push(FilterOperator::Single(filter));
}
if filters.len() == 1 {
filters.pop().unwrap()
} else {
FilterOperator::Or(filters)
}
}
pub fn new_single_field(field: impl Into<String>, query: impl Into<String>) -> Self {
Self {
query: query.into(),
columns: vec![field.into()],
case_sensitive: false,
exact_match: false,
}
}
pub fn new_multiple_fields(fields: Vec<impl Into<String>>, query: impl Into<String>) -> Self {
Self {
query: query.into(),
columns: fields.into_iter().map(|f| f.into()).collect(),
case_sensitive: false,
exact_match: false,
}
}
pub fn to_filter_operator_improved(&self) -> FilterOperator {
let mut filters = Vec::new();
for column in &self.columns {
let pattern = if self.exact_match {
self.query.clone()
} else {
format!("%{}%", self.query)
};
let filter = if self.case_sensitive {
Filter::like(column.clone(), pattern)
} else {
Filter::like(column.clone(), pattern)
};
filters.push(FilterOperator::Single(filter));
}
if filters.len() == 1 {
filters.pop().unwrap()
} else {
FilterOperator::Or(filters)
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sort {
pub column: String,
pub order: crate::SortOrder,
}
impl Sort {
pub fn new(column: impl Into<String>, order: crate::SortOrder) -> Self {
Self {
column: column.into(),
order,
}
}
pub fn new_bool(column: impl Into<String>, ascending: bool) -> Self {
Self {
column: column.into(),
order: if ascending {
crate::SortOrder::Asc
} else {
crate::SortOrder::Desc
},
}
}
pub fn asc(column: impl Into<String>) -> Self {
Self::new(column, crate::SortOrder::Asc)
}
pub fn desc(column: impl Into<String>) -> Self {
Self::new(column, crate::SortOrder::Desc)
}
}