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
119
120
121
122
123
124
125
mod arc;
mod mock;
mod rc;
mod tuple;

/// Trait for types that represent the logical state of an application.
///
/// Perhaps a more accurate mental model for types that implement this trait is that of a
/// _state machine_, where the nodes correspond to the universe of all possible representable
/// values and the edges correspond to [_actions_](trait.Reducer.html#associatedtype.Action).
///
/// Types that implement this trait must be self-contained and should not depend on any external
/// state, hence the required `'static` bound.
///
/// # Splitting Up State Logic
/// Handling the entire state and its transitions in a single Reducer quickly grows out of hand for
/// any meaningful application. As the complexity of your application grows, it's a good idea to
/// break up the state into smaller independent pieces. To help assembling the pieces back
/// together, Reducer is implicitly implemented for tuples.
///
/// ## Example
/// ```rust
/// use reducer::Reducer;
///
/// struct ProductListing { /* ... */ }
/// struct ShoppingCart { /* ... */ }
///
/// #[derive(Clone)]
/// struct AddToCart( /* ... */ );
///
/// impl Reducer<AddToCart> for ProductListing {
///     fn reduce(&mut self, action: AddToCart) {
///         // ...
///     }
/// }
///
/// impl Reducer<AddToCart> for ShoppingCart {
///     fn reduce(&mut self, action: AddToCart) {
///         // ...
///     }
/// }
///
/// let products = ProductListing { /* ... */ };
/// let cart = ShoppingCart { /* ... */ };
/// let mut shop = (products, cart);
///
/// // `shop` itself implements Reducer<AddToCart>
/// shop.reduce(AddToCart( /* ... */ ));
/// ```

pub trait Reducer<A>: 'static {
    /// Implements the transition given the current state and an action.
    ///
    /// This method is expected to have no side effects and must never fail.
    /// In many cases, an effective way to handle illegal state transitions is to make
    /// them idempotent, that is to leave the state unchanged.
    ///
    /// # Example
    /// ```rust
    /// use reducer::Reducer;
    ///
    /// #[derive(Debug)]
    /// struct Todos(Vec<String>);
    ///
    /// // Actions
    /// struct Create(String);
    /// struct Remove(usize);
    ///
    /// impl Reducer<Create> for Todos {
    ///     fn reduce(&mut self, Create(todo): Create) {
    ///         self.0.push(todo);
    ///     }
    /// }
    ///
    /// impl Reducer<Remove> for Todos {
    ///     fn reduce(&mut self, Remove(i): Remove) {
    ///         if i < self.0.len() {
    ///             self.0.remove(i);
    ///         } else {
    ///             // Illegal transition, leave the state unchanged.
    ///         }
    ///     }
    /// }
    ///
    /// fn main() {
    ///     let mut todos = Todos(vec![]);
    ///
    ///     todos.reduce(Create("Buy milk".to_string()));
    ///     println!("{:?}", todos); // ["Buy milk"]
    ///
    ///     todos.reduce(Create("Learn Reducer".to_string()));
    ///     println!("{:?}", todos); // ["Buy milk", "Learn Reducer"]
    ///
    ///     todos.reduce(Remove(42)); // out of bounds
    ///     println!("{:?}", todos); // ["Buy milk", "Learn Reducer"]
    ///
    ///     todos.reduce(Remove(0));
    ///     println!("{:?}", todos); // ["Learn Reducer"]
    /// }
    /// ```
    fn reduce(&mut self, action: A);
}

#[cfg(test)]
pub use self::mock::*;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn reduce() {
        let mut mock = MockReducer::default();

        {
            let state: &mut Reducer<_> = &mut mock;

            state.reduce(5);
            state.reduce(1);
            state.reduce(3);
        }

        assert_eq!(mock, MockReducer::new(vec![5, 1, 3]));
    }
}