1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use crate::reactor::*;

macro_rules! impl_reactor_for_tuple {
    ( $($args:ident,)+ ) => {
        /// Notifies all [`Reactor`]s in the tuple in order.
        ///
        /// <small>Currently implemented for tuples of up to 12 elements.</small>
        ///
        /// # Example
        ///
        /// ```rust
        /// use reducer::*;
        /// use std::error::Error;
        ///
        /// #[derive(Debug)]
        /// struct State { /* ... */ }
        /// struct Action { /* ... */ }
        ///
        /// impl Reducer<Action> for State {
        ///     fn reduce(&mut self, action: Action) {
        ///         // ...
        ///     }
        /// }
        ///
        /// struct GUI { /* ... */ }
        /// struct DebugLogger { /* ... */ }
        ///
        /// impl Reactor<State> for GUI {
        ///     type Error = Box<dyn Error>;
        ///     fn react(&mut self, state: &State) -> Result<(), Self::Error> {
        ///         // ...
        ///         Ok(())
        ///     }
        /// }
        ///
        /// impl Reactor<State> for DebugLogger {
        ///     type Error = Box<dyn Error>;
        ///     fn react(&mut self, state: &State) -> Result<(), Self::Error> {
        ///         println!("[DEBUG] {:#?}", state);
        ///         Ok(())
        ///     }
        /// }
        ///
        /// let gui = GUI { /* ... */ };
        /// let logger = DebugLogger { /* ... */ };
        ///
        /// let mut store = Store::new(State { /* ... */ }, (gui, logger));
        ///
        /// // Both `gui` and `logger` get notified of state changes.
        /// store.dispatch(Action { /* ... */ });
        /// ```
        impl<S, X, $($args,)+> Reactor<S> for ($($args,)+)
        where
            S: ?Sized,
            $($args: Reactor<S, Error = X>,)+
        {
            type Error = X;

            fn react(&mut self, state: &S) -> Result<(), Self::Error> {
                #[allow(non_snake_case)]
                let ($($args,)+) = self;
                $($args.react(state)?;)+
                Ok(())
            }
        }
    };
}

macro_rules! impl_reactor_for_tuples {
    () => {};

    ( $head:ident $(, $tail:ident)* $(,)? ) => {
        impl_reactor_for_tuples!($($tail,)*);
        reverse!(impl_reactor_for_tuple!($head $(, $tail)*));
    };
}

impl_reactor_for_tuples!(L, K, J, I, H, G, F, E, D, C, B, A);

#[cfg(test)]
mod tests {
    use super::*;
    use mockall::predicate::*;
    use test_strategy::proptest;

    macro_rules! test_reactor_for_tuples {
        () => {};

        ( $head:ident $(, $tail:ident)* $(,)? ) => {
            #[proptest]
            fn $head(state: u8, results: [Result<(), u8>; count!($($tail,)*) + 1]) {
                let (idx, result) = results
                    .iter()
                    .enumerate()
                    .find(|(_, r)| r.is_err())
                    .map_or((results.len(), Ok(())), |(i, &r)| (i, r));

                let mut mocks: [MockReactor<_, _>; count!($($tail,)*) + 1] = Default::default();

                for (i, (mock, &result)) in mocks.iter_mut().zip(&results).enumerate() {
                    mock.expect_react()
                        .with(eq(state))
                        .times(if i > idx { 0 } else { 1 })
                        .return_const(result);
                }

                let [$head, $($tail,)*] = mocks;
                let mut reactor = ($head, $($tail,)*);
                assert_eq!(Reactor::react(&mut reactor, &state), result);

            }

            test_reactor_for_tuples!($($tail,)*);
        };
    }

    test_reactor_for_tuples!(_12, _11, _10, _09, _08, _07, _06, _05, _04, _03, _02, _01);
}