rust_overture/
concat.rs

1/// Concatenate pure functions (A -> A).
2pub fn concat_fn<A>(
3    fs: Vec<Box<dyn Fn(A) -> A>>
4) -> impl Fn(A) -> A {
5    move |mut a: A| {
6        for f in &fs {
7            a = f(a);
8        }
9        a
10    }
11}
12
13/// Concatenate throwing functions (A -> Result<A, E>).
14pub fn concat_fn_result<A, E>(
15    fs: Vec<Box<dyn Fn(A) -> Result<A, E>>>
16) -> impl Fn(A) -> Result<A, E> {
17    move |mut a: A| {
18        for f in &fs {
19            a = f(a)?;
20        }
21        Ok(a)
22    }
23}
24
25/// Concatenate mutating functions (FnMut(&mut A)).
26pub fn concat_mut<A>(
27    mut fs: Vec<Box<dyn FnMut(&mut A)>>
28) -> impl FnMut(&mut A) {
29    move |a: &mut A| {
30        for f in &mut fs {
31            f(a);
32        }
33    }
34}
35
36/// Concatenate throwing mutating functions (FnMut(&mut A) -> Result<(), E>).
37pub fn concat_mut_result<A, E>(
38    mut fs: Vec<Box<dyn FnMut(&mut A) -> Result<(), E>>>
39) -> impl FnMut(&mut A) -> Result<(), E> {
40    move |a: &mut A| {
41        for f in &mut fs {
42            f(a)?;
43        }
44        Ok(())
45    }
46}
47
48// ---- Separate macros ----
49
50#[macro_export]
51macro_rules! concat_fn {
52    ($($f:expr),+ $(,)?) => {{
53        let funcs: Vec<Box<dyn Fn(_) -> _>> = vec![$(Box::new($f)),+];
54        concat_fn(funcs)
55    }};
56}
57
58#[macro_export]
59macro_rules! concat_tryfn {
60    ($($f:expr),+ $(,)?) => {{
61        let funcs: Vec<Box<dyn Fn(_) -> Result<_, _>>> = vec![$(Box::new($f)),+];
62        concat_fn_result(funcs)
63    }};
64}
65
66#[macro_export]
67macro_rules! concat_mut {
68    ($($f:expr),+ $(,)?) => {{
69        let funcs: Vec<Box<dyn FnMut(&mut _)>> = vec![$(Box::new($f)),+];
70        concat_mut(funcs)
71    }};
72}
73
74#[macro_export]
75macro_rules! concat_trymut {
76    ($($f:expr),+ $(,)?) => {{
77        let funcs: Vec<Box<dyn FnMut(&mut _) -> Result<(), _>>> = vec![$(Box::new($f)),+];
78        concat_mut_result(funcs)
79    }};
80}
81
82// ---- Tests ----
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn test_concat_fn() {
89        let f = concat_fn!(|x: i32| x + 1, |x| x * 2, |x| x - 3);
90        assert_eq!(f(2), 3); // ((2+1) * 2) - 3 = 3
91    }
92
93    #[test]
94    fn test_concat_tryfn() {
95        let f = concat_tryfn!(
96            |x: i32| if x > 0 { Ok(x + 1) } else { Err("neg") },
97            |x| Ok(x * 2)
98        );
99        assert_eq!(f(1), Ok(4));
100        assert_eq!(f(-1), Err("neg"));
101    }
102
103    #[test]
104    fn test_concat_mut() {
105        let mut f = concat_mut!(|x: &mut i32| *x += 2, |x: &mut i32| *x *= 3);
106        let mut val = 1;
107        f(&mut val);
108        assert_eq!(val, 9);
109    }
110
111    #[test]
112    fn test_concat_trymut() {
113        let mut f = concat_trymut!(
114            |x: &mut i32| if *x > 0 { *x += 1; Ok(()) } else { Err("bad") },
115            |x: &mut i32| { *x *= 2; Ok(()) }
116        );
117        let mut val = 2;
118        assert_eq!(f(&mut val), Ok(()));
119        assert_eq!(val, 6);
120        let mut neg = -1;
121        assert_eq!(f(&mut neg), Err("bad"));
122    }
123}