use crate::step::Step;
use crate::transducer::Transducer;
use std::marker::PhantomData;
use std::rc::Rc;
pub type BoxedPredicate<T> = Box<dyn Fn(&T) -> bool>;
pub type PredicateVec<T> = Vec<BoxedPredicate<T>>;
pub fn both<T, P1, P2>(pred1: P1, pred2: P2) -> impl Fn(&T) -> bool
where
P1: Fn(&T) -> bool,
P2: Fn(&T) -> bool,
{
move |x| pred1(x) && pred2(x)
}
pub fn either<T, P1, P2>(pred1: P1, pred2: P2) -> impl Fn(&T) -> bool
where
P1: Fn(&T) -> bool,
P2: Fn(&T) -> bool,
{
move |x| pred1(x) || pred2(x)
}
pub fn complement<T, P>(pred: P) -> impl Fn(&T) -> bool
where
P: Fn(&T) -> bool,
{
move |x| !pred(x)
}
pub fn all_pass<T>(predicates: PredicateVec<T>) -> impl Fn(&T) -> bool {
move |x| predicates.iter().all(|pred| pred(x))
}
pub fn any_pass<T>(predicates: PredicateVec<T>) -> impl Fn(&T) -> bool {
move |x| predicates.iter().any(|pred| pred(x))
}
pub struct When<P, F, T> {
predicate: Rc<P>,
transform: Rc<F>,
_phantom: PhantomData<T>,
}
impl<P, F, T> When<P, F, T>
where
P: Fn(&T) -> bool,
F: Fn(T) -> T,
{
pub fn new(predicate: P, transform: F) -> Self {
When {
predicate: Rc::new(predicate),
transform: Rc::new(transform),
_phantom: PhantomData,
}
}
}
impl<P, F, T> Transducer<T, T> for When<P, F, T>
where
P: Fn(&T) -> bool + 'static,
F: Fn(T) -> T + 'static,
T: Clone + 'static,
{
#[inline(always)]
fn apply<Acc, R>(&self, reducer: R) -> Box<dyn Fn(Acc, T) -> Step<Acc>>
where
R: Fn(Acc, T) -> Step<Acc> + 'static,
Acc: 'static,
{
let predicate = Rc::clone(&self.predicate);
let transform = Rc::clone(&self.transform);
Box::new(move |acc, val| {
if predicate(&val) {
reducer(acc, transform(val))
} else {
reducer(acc, val)
}
})
}
}
pub struct Unless<P, F, T> {
predicate: Rc<P>,
transform: Rc<F>,
_phantom: PhantomData<T>,
}
impl<P, F, T> Unless<P, F, T>
where
P: Fn(&T) -> bool,
F: Fn(T) -> T,
{
pub fn new(predicate: P, transform: F) -> Self {
Unless {
predicate: Rc::new(predicate),
transform: Rc::new(transform),
_phantom: PhantomData,
}
}
}
impl<P, F, T> Transducer<T, T> for Unless<P, F, T>
where
P: Fn(&T) -> bool + 'static,
F: Fn(T) -> T + 'static,
T: Clone + 'static,
{
#[inline(always)]
fn apply<Acc, R>(&self, reducer: R) -> Box<dyn Fn(Acc, T) -> Step<Acc>>
where
R: Fn(Acc, T) -> Step<Acc> + 'static,
Acc: 'static,
{
let predicate = Rc::clone(&self.predicate);
let transform = Rc::clone(&self.transform);
Box::new(move |acc, val| {
if !predicate(&val) {
reducer(acc, transform(val))
} else {
reducer(acc, val)
}
})
}
}
pub struct IfElse<P, F1, F2, T> {
predicate: Rc<P>,
on_true: Rc<F1>,
on_false: Rc<F2>,
_phantom: PhantomData<T>,
}
impl<P, F1, F2, T> IfElse<P, F1, F2, T>
where
P: Fn(&T) -> bool,
F1: Fn(T) -> T,
F2: Fn(T) -> T,
{
pub fn new(predicate: P, on_true: F1, on_false: F2) -> Self {
IfElse {
predicate: Rc::new(predicate),
on_true: Rc::new(on_true),
on_false: Rc::new(on_false),
_phantom: PhantomData,
}
}
}
impl<P, F1, F2, T> Transducer<T, T> for IfElse<P, F1, F2, T>
where
P: Fn(&T) -> bool + 'static,
F1: Fn(T) -> T + 'static,
F2: Fn(T) -> T + 'static,
T: Clone + 'static,
{
#[inline(always)]
fn apply<Acc, R>(&self, reducer: R) -> Box<dyn Fn(Acc, T) -> Step<Acc>>
where
R: Fn(Acc, T) -> Step<Acc> + 'static,
Acc: 'static,
{
let predicate = Rc::clone(&self.predicate);
let on_true = Rc::clone(&self.on_true);
let on_false = Rc::clone(&self.on_false);
Box::new(move |acc, val| {
if predicate(&val) {
reducer(acc, on_true(val))
} else {
reducer(acc, on_false(val))
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::collectors::to_vec;
#[test]
fn test_both() {
let is_positive = |x: &i32| *x > 0;
let is_even = |x: &i32| x % 2 == 0;
let is_positive_even = both(is_positive, is_even);
assert!(is_positive_even(&4));
assert!(!is_positive_even(&3));
assert!(!is_positive_even(&-2));
}
#[test]
fn test_either() {
let is_small = |x: &i32| *x < 10;
let is_large = |x: &i32| *x > 100;
let is_extreme = either(is_small, is_large);
assert!(is_extreme(&5));
assert!(is_extreme(&200));
assert!(!is_extreme(&50));
}
#[test]
fn test_complement() {
let is_even = |x: &i32| x % 2 == 0;
let is_odd = complement(is_even);
assert!(is_odd(&3));
assert!(!is_odd(&4));
}
#[test]
fn test_all_pass() {
let predicates: PredicateVec<i32> = vec![
Box::new(|x: &i32| *x > 0),
Box::new(|x: &i32| *x < 100),
Box::new(|x: &i32| x % 2 == 0),
];
let is_valid = all_pass(predicates);
assert!(is_valid(&50));
assert!(!is_valid(&3));
assert!(!is_valid(&150));
}
#[test]
fn test_any_pass() {
let predicates: PredicateVec<i32> = vec![
Box::new(|x: &i32| *x == 0),
Box::new(|x: &i32| x % 10 == 0),
Box::new(|x: &i32| *x > 1000),
];
let is_special = any_pass(predicates);
assert!(is_special(&0));
assert!(is_special(&50));
assert!(is_special(&2000));
assert!(!is_special(&7));
}
#[test]
fn test_when() {
let double_if_positive = When::new(|x: &i32| *x > 0, |x: i32| x * 2);
let result = to_vec(&double_if_positive, vec![-1, 2, -3, 4]);
assert_eq!(result, vec![-1, 4, -3, 8]);
}
#[test]
fn test_unless() {
let zero_if_negative = Unless::new(|x: &i32| *x > 0, |_| 0);
let result = to_vec(&zero_if_negative, vec![-1, 2, -3, 4]);
assert_eq!(result, vec![0, 2, 0, 4]);
}
#[test]
fn test_if_else() {
let abs_with_sign = IfElse::new(|x: &i32| *x >= 0, |x: i32| x * 2, |x: i32| x / 2);
let result = to_vec(&abs_with_sign, vec![-4, 3, -6, 5]);
assert_eq!(result, vec![-2, 6, -3, 10]);
}
}