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]));
}
}