1use crate::contravariant::{Contravariant, PredicateF};
5#[cfg(all(not(feature = "std"), feature = "alloc"))]
6use alloc::boxed::Box;
7
8pub trait Decide: Contravariant {
17 fn choose<A: 'static, B: 'static, C: 'static>(
18 f: impl Fn(C) -> Result<A, B> + 'static,
19 fa: Self::Of<A>,
20 fb: Self::Of<B>,
21 ) -> Self::Of<C>;
22}
23
24impl Decide for PredicateF {
25 fn choose<A: 'static, B: 'static, C: 'static>(
26 f: impl Fn(C) -> Result<A, B> + 'static,
27 fa: Box<dyn Fn(A) -> bool>,
28 fb: Box<dyn Fn(B) -> bool>,
29 ) -> Box<dyn Fn(C) -> bool> {
30 Box::new(move |c| match f(c) {
31 Ok(a) => fa(a),
32 Err(b) => fb(b),
33 })
34 }
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40
41 #[test]
42 fn predicate_choose() {
43 let is_positive: Box<dyn Fn(i32) -> bool> = Box::new(|x| x > 0);
44 let is_short: Box<dyn Fn(String) -> bool> = Box::new(|s| s.len() < 5);
45
46 let classifier =
48 PredicateF::choose(|input: Result<i32, String>| input, is_positive, is_short);
49
50 assert!(classifier(Ok(5)));
51 assert!(!classifier(Ok(-1)));
52 assert!(classifier(Err("hi".to_string())));
53 assert!(!classifier(Err("hello world".to_string())));
54 }
55}
56
57#[cfg(test)]
58mod law_tests {
59 use super::*;
60 use proptest::prelude::*;
61
62 proptest! {
63 #[test]
65 fn predicate_associativity(x in any::<i8>(), y in any::<i8>(), z in any::<i8>()) {
66 let pa: Box<dyn Fn(i8) -> bool> = Box::new(|a| a > 0);
67 let pb: Box<dyn Fn(i8) -> bool> = Box::new(|b| b > 0);
68 let pc: Box<dyn Fn(i8) -> bool> = Box::new(|c| c > 0);
69
70 let pa2: Box<dyn Fn(i8) -> bool> = Box::new(|a| a > 0);
71 let pb2: Box<dyn Fn(i8) -> bool> = Box::new(|b| b > 0);
72 let pc2: Box<dyn Fn(i8) -> bool> = Box::new(|c| c > 0);
73
74 let ab = PredicateF::choose(|v: Result<i8, i8>| v, pa, pb);
77 let left = PredicateF::choose(
78 |tag: (u8, i8)| {
79 if tag.0 < 2 {
80 Ok(if tag.0 == 0 { Ok(tag.1) } else { Err(tag.1) })
81 } else {
82 Err(tag.1)
83 }
84 },
85 ab,
86 pc,
87 );
88
89 let bc = PredicateF::choose(|v: Result<i8, i8>| v, pb2, pc2);
90 let right = PredicateF::choose(
91 |tag: (u8, i8)| {
92 if tag.0 == 0 {
93 Ok(tag.1)
94 } else {
95 Err(if tag.0 == 1 { Ok(tag.1) } else { Err(tag.1) })
96 }
97 },
98 pa2,
99 bc,
100 );
101
102 prop_assert_eq!(left((0, x)), right((0, x)));
104 prop_assert_eq!(left((1, y)), right((1, y)));
105 prop_assert_eq!(left((2, z)), right((2, z)));
106 }
107 }
108}