pub trait Predicate<T: ?Sized>: Send + Sync {
fn check(&self, value: &T) -> bool;
}
impl<T: ?Sized, F> Predicate<T> for F
where
F: Fn(&T) -> bool + Send + Sync,
{
#[inline]
fn check(&self, value: &T) -> bool {
self(value)
}
}
pub trait PredicateExt<T: ?Sized>: Predicate<T> + Sized {
fn and<P: Predicate<T>>(self, other: P) -> And<Self, P> {
And(self, other)
}
fn or<P: Predicate<T>>(self, other: P) -> Or<Self, P> {
Or(self, other)
}
fn not(self) -> Not<Self> {
Not(self)
}
}
impl<T: ?Sized, P: Predicate<T>> PredicateExt<T> for P {}
#[derive(Clone, Copy, Debug)]
pub struct And<P1, P2>(pub P1, pub P2);
impl<T: ?Sized, P1: Predicate<T>, P2: Predicate<T>> Predicate<T> for And<P1, P2> {
#[inline]
fn check(&self, value: &T) -> bool {
self.0.check(value) && self.1.check(value)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Or<P1, P2>(pub P1, pub P2);
impl<T: ?Sized, P1: Predicate<T>, P2: Predicate<T>> Predicate<T> for Or<P1, P2> {
#[inline]
fn check(&self, value: &T) -> bool {
self.0.check(value) || self.1.check(value)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Not<P>(pub P);
impl<T: ?Sized, P: Predicate<T>> Predicate<T> for Not<P> {
#[inline]
fn check(&self, value: &T) -> bool {
!self.0.check(value)
}
}
#[derive(Clone, Copy, Debug)]
pub struct AllOf<P, const N: usize>(pub [P; N]);
impl<T: ?Sized, P: Predicate<T>, const N: usize> Predicate<T> for AllOf<P, N> {
#[inline]
fn check(&self, value: &T) -> bool {
self.0.iter().all(|p| p.check(value))
}
}
pub fn all_of<P, const N: usize>(predicates: [P; N]) -> AllOf<P, N> {
AllOf(predicates)
}
#[derive(Clone, Copy, Debug)]
pub struct AnyOf<P, const N: usize>(pub [P; N]);
impl<T: ?Sized, P: Predicate<T>, const N: usize> Predicate<T> for AnyOf<P, N> {
#[inline]
fn check(&self, value: &T) -> bool {
self.0.iter().any(|p| p.check(value))
}
}
pub fn any_of<P, const N: usize>(predicates: [P; N]) -> AnyOf<P, N> {
AnyOf(predicates)
}
#[derive(Clone, Copy, Debug)]
pub struct NoneOf<P, const N: usize>(pub [P; N]);
impl<T: ?Sized, P: Predicate<T>, const N: usize> Predicate<T> for NoneOf<P, N> {
#[inline]
fn check(&self, value: &T) -> bool {
!self.0.iter().any(|p| p.check(value))
}
}
pub fn none_of<P, const N: usize>(predicates: [P; N]) -> NoneOf<P, N> {
NoneOf(predicates)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::predicate::{eq, gt, lt, positive};
#[test]
fn test_and() {
let p = gt(0).and(lt(10));
assert!(p.check(&5));
assert!(!p.check(&0));
assert!(!p.check(&10));
}
#[test]
fn test_or() {
let p = lt(0).or(gt(100));
assert!(p.check(&-5));
assert!(p.check(&150));
assert!(!p.check(&50));
}
#[test]
fn test_not() {
let p = positive::<i32>().not();
assert!(p.check(&-5));
assert!(p.check(&0));
assert!(!p.check(&5));
}
#[test]
fn test_all_of() {
let bounds = gt(0).and(lt(100));
assert!(bounds.check(&50));
assert!(!bounds.check(&0));
assert!(!bounds.check(&100));
let greater_than_bounds = all_of([gt(0), gt(-10), gt(-100)]);
assert!(greater_than_bounds.check(&50));
assert!(!greater_than_bounds.check(&-50));
}
#[test]
fn test_any_of() {
let p = any_of([eq(1), eq(5), eq(10)]);
assert!(p.check(&1));
assert!(p.check(&5));
assert!(p.check(&10));
assert!(!p.check(&2));
}
#[test]
fn test_none_of() {
let p = none_of([eq(1), eq(5), eq(10)]);
assert!(!p.check(&1));
assert!(!p.check(&5));
assert!(p.check(&2));
assert!(p.check(&7));
}
#[test]
fn test_complex_chain() {
let p = gt(0).and(lt(10)).or(gt(100)).not();
assert!(p.check(&0)); assert!(p.check(&50)); assert!(!p.check(&5)); assert!(!p.check(&150)); }
#[test]
fn test_closure_as_predicate() {
let is_even = |x: &i32| x % 2 == 0;
assert!(is_even.check(&4));
assert!(!is_even.check(&3));
let even_and_positive = is_even.and(positive());
assert!(even_and_positive.check(&4));
assert!(!even_and_positive.check(&-4));
}
}