use std::fmt;
use std::marker::PhantomData;
use regex::{Regex, RegexBuilder};
use super::{
__private, FieldId, FilterEnum, FilterExpr, FilterExpressionError, FilterExpressionErrorKind,
PredicateData, RelationPredicate, RelationQuantifier, SetOperator, SetPredicate, TextOperator,
TextPredicate,
};
use crate::TmuxText;
pub struct TextField<T, V = TmuxText> {
pub(super) id: FieldId,
pub(super) marker: PhantomData<fn() -> (T, V)>,
}
pub struct BoolField<T> {
pub(super) id: FieldId,
pub(super) marker: PhantomData<fn() -> T>,
}
pub struct IntegerField<T, N> {
pub(super) id: FieldId,
pub(super) marker: PhantomData<fn() -> (T, N)>,
}
pub struct EnumField<T, E> {
pub(super) id: FieldId,
pub(super) marker: PhantomData<fn() -> (T, E)>,
}
pub struct ManyRelation<From, To> {
pub(super) id: FieldId,
pub(super) marker: PhantomData<fn() -> (From, To)>,
}
pub struct OneRelation<From, To> {
pub(super) id: FieldId,
pub(super) marker: PhantomData<fn() -> (From, To)>,
}
impl<From, To> ManyRelation<From, To> {
fn expression(
self,
quantifier: RelationQuantifier,
expression: FilterExpr<To>,
) -> FilterExpr<From> {
FilterExpr::predicate(__private::Predicate::new(
self.id,
PredicateData::Relation(RelationPredicate {
quantifier,
expression: Box::new(expression.data),
}),
))
}
#[must_use]
pub fn any(self, expression: FilterExpr<To>) -> FilterExpr<From> {
self.expression(RelationQuantifier::Any, expression)
}
#[must_use]
pub fn all(self, expression: FilterExpr<To>) -> FilterExpr<From> {
self.expression(RelationQuantifier::All, expression)
}
#[must_use]
pub fn none(self, expression: FilterExpr<To>) -> FilterExpr<From> {
self.expression(RelationQuantifier::None, expression)
}
}
impl<From, To> OneRelation<From, To> {
#[must_use]
pub fn is(self, expression: FilterExpr<To>) -> FilterExpr<From> {
FilterExpr::predicate(__private::Predicate::new(
self.id,
PredicateData::Relation(RelationPredicate {
quantifier: RelationQuantifier::Is,
expression: Box::new(expression.data),
}),
))
}
}
macro_rules! impl_handle_traits {
($name:ident<$($type_parameter:ident),+>) => {
impl<$($type_parameter),+> $name<$($type_parameter),+> {
#[allow(dead_code, reason = "only snapshot reads ask a handle its name")]
pub(crate) const fn field_name(self) -> &'static str {
self.id.field
}
}
impl<$($type_parameter),+> Copy for $name<$($type_parameter),+> {}
impl<$($type_parameter),+> Clone for $name<$($type_parameter),+> {
fn clone(&self) -> Self {
*self
}
}
impl<$($type_parameter),+> PartialEq for $name<$($type_parameter),+> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<$($type_parameter),+> Eq for $name<$($type_parameter),+> {}
impl<$($type_parameter),+> fmt::Debug for $name<$($type_parameter),+> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct(stringify!($name))
.field("target", &self.id.target)
.field("field", &self.id.field)
.finish()
}
}
};
}
impl_handle_traits!(TextField<T, V>);
impl_handle_traits!(BoolField<T>);
impl_handle_traits!(IntegerField<T, N>);
impl_handle_traits!(EnumField<T, E>);
impl_handle_traits!(ManyRelation<From, To>);
impl_handle_traits!(OneRelation<From, To>);
impl<T, V> TextField<T, V> {
pub(crate) const fn typed(target: &'static str, field: &'static str) -> Self {
Self {
id: FieldId { target, field },
marker: PhantomData,
}
}
fn expression(
self,
operator: TextOperator,
values: Vec<String>,
compiled_regex: Option<Regex>,
) -> FilterExpr<T> {
FilterExpr::predicate(__private::Predicate::new(
self.id,
PredicateData::Text(TextPredicate {
operator,
values,
compiled_regex,
}),
))
}
fn one(self, operator: TextOperator, value: impl Into<String>) -> FilterExpr<T> {
self.expression(operator, vec![value.into()], None)
}
#[must_use]
pub fn eq(self, value: impl Into<String>) -> FilterExpr<T> {
self.one(TextOperator::Eq, value)
}
#[must_use]
pub fn eq_ignore_case(self, value: impl Into<String>) -> FilterExpr<T> {
self.one(TextOperator::EqIgnoreCase, value)
}
#[must_use]
pub fn contains(self, value: impl Into<String>) -> FilterExpr<T> {
self.one(TextOperator::Contains, value)
}
#[must_use]
pub fn contains_ignore_case(self, value: impl Into<String>) -> FilterExpr<T> {
self.one(TextOperator::ContainsIgnoreCase, value)
}
#[must_use]
pub fn starts_with(self, value: impl Into<String>) -> FilterExpr<T> {
self.one(TextOperator::StartsWith, value)
}
#[must_use]
pub fn starts_with_ignore_case(self, value: impl Into<String>) -> FilterExpr<T> {
self.one(TextOperator::StartsWithIgnoreCase, value)
}
#[must_use]
pub fn ends_with(self, value: impl Into<String>) -> FilterExpr<T> {
self.one(TextOperator::EndsWith, value)
}
#[must_use]
pub fn ends_with_ignore_case(self, value: impl Into<String>) -> FilterExpr<T> {
self.one(TextOperator::EndsWithIgnoreCase, value)
}
#[must_use]
pub fn is_in<I, S>(self, values: I) -> FilterExpr<T>
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.expression(
TextOperator::In,
values.into_iter().map(Into::into).collect(),
None,
)
}
#[must_use]
pub fn not_in<I, S>(self, values: I) -> FilterExpr<T>
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.expression(
TextOperator::NotIn,
values.into_iter().map(Into::into).collect(),
None,
)
}
fn regex_inner(
self,
pattern: String,
ignore_case: bool,
) -> Result<FilterExpr<T>, FilterExpressionError> {
let compiled_regex = RegexBuilder::new(&pattern)
.case_insensitive(ignore_case)
.build()
.map_err(|_| FilterExpressionError::new(FilterExpressionErrorKind::InvalidRegex))?;
let operator = if ignore_case {
TextOperator::RegexIgnoreCase
} else {
TextOperator::Regex
};
Ok(self.expression(operator, vec![pattern], Some(compiled_regex)))
}
pub fn regex(self, pattern: impl Into<String>) -> Result<FilterExpr<T>, FilterExpressionError> {
self.regex_inner(pattern.into(), false)
}
pub fn regex_ignore_case(
self,
pattern: impl Into<String>,
) -> Result<FilterExpr<T>, FilterExpressionError> {
self.regex_inner(pattern.into(), true)
}
}
impl<T> BoolField<T> {
fn expression(self, operator: SetOperator, values: Vec<bool>) -> FilterExpr<T> {
FilterExpr::predicate(__private::Predicate::new(
self.id,
PredicateData::Bool(SetPredicate { operator, values }),
))
}
#[must_use]
pub fn eq(self, value: bool) -> FilterExpr<T> {
self.expression(SetOperator::Eq, vec![value])
}
#[must_use]
pub fn is_in(self, values: impl IntoIterator<Item = bool>) -> FilterExpr<T> {
self.expression(SetOperator::In, values.into_iter().collect())
}
#[must_use]
pub fn not_in(self, values: impl IntoIterator<Item = bool>) -> FilterExpr<T> {
self.expression(SetOperator::NotIn, values.into_iter().collect())
}
}
macro_rules! impl_integer_field {
($integer:ty, $variant:ident, $convert:path) => {
impl<T> IntegerField<T, $integer> {
fn expression(self, operator: SetOperator, values: Vec<$integer>) -> FilterExpr<T> {
FilterExpr::predicate(__private::Predicate::new(
self.id,
PredicateData::$variant(SetPredicate {
operator,
values: values.into_iter().map($convert).collect(),
}),
))
}
#[must_use]
pub fn eq(self, value: $integer) -> FilterExpr<T> {
self.expression(SetOperator::Eq, vec![value])
}
#[must_use]
pub fn is_in(self, values: impl IntoIterator<Item = $integer>) -> FilterExpr<T> {
self.expression(SetOperator::In, values.into_iter().collect())
}
#[must_use]
pub fn not_in(self, values: impl IntoIterator<Item = $integer>) -> FilterExpr<T> {
self.expression(SetOperator::NotIn, values.into_iter().collect())
}
#[must_use]
pub fn lt(self, bound: $integer) -> FilterExpr<T> {
self.expression(SetOperator::Lt, vec![bound])
}
#[must_use]
pub fn lte(self, bound: $integer) -> FilterExpr<T> {
self.expression(SetOperator::Lte, vec![bound])
}
#[must_use]
pub fn gt(self, bound: $integer) -> FilterExpr<T> {
self.expression(SetOperator::Gt, vec![bound])
}
#[must_use]
pub fn gte(self, bound: $integer) -> FilterExpr<T> {
self.expression(SetOperator::Gte, vec![bound])
}
}
};
}
impl_integer_field!(i8, Signed, i128::from);
impl_integer_field!(i16, Signed, i128::from);
impl_integer_field!(i32, Signed, i128::from);
impl_integer_field!(i64, Signed, i128::from);
impl_integer_field!(i128, Signed, i128::from);
impl_integer_field!(u8, Unsigned, u128::from);
impl_integer_field!(u16, Unsigned, u128::from);
impl_integer_field!(u32, Unsigned, u128::from);
impl_integer_field!(u64, Unsigned, u128::from);
impl_integer_field!(u128, Unsigned, u128::from);
impl<T, E: FilterEnum> EnumField<T, E> {
fn expression(self, operator: SetOperator, values: Vec<E>) -> FilterExpr<T> {
FilterExpr::predicate(__private::Predicate::new(
self.id,
PredicateData::Enum(SetPredicate {
operator,
values: values
.into_iter()
.map(|value| value.filter_name().to_owned())
.collect(),
}),
))
}
#[must_use]
pub fn eq(self, value: E) -> FilterExpr<T> {
self.expression(SetOperator::Eq, vec![value])
}
#[must_use]
pub fn is_in(self, values: impl IntoIterator<Item = E>) -> FilterExpr<T> {
self.expression(SetOperator::In, values.into_iter().collect())
}
#[must_use]
pub fn not_in(self, values: impl IntoIterator<Item = E>) -> FilterExpr<T> {
self.expression(SetOperator::NotIn, values.into_iter().collect())
}
}