rust_overture/
concat.rs

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