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
use crate::reducer::*;
use alloc::rc::Rc;
/// Enhances a potentially _unsized_ [`Reducer`] with copy-on-write semantics (requires [`alloc`]).
///
/// Helps avoiding cloning the entire state when it needs to be sent to other parts of the
/// application.
///
/// [`alloc`]: index.html#optional-features
///
/// # Example
///
/// ```rust
/// use reducer::*;
/// use std::rc::Rc;
///
/// #[derive(Clone)]
/// struct State { /* ... */ }
/// struct Action { /* ... */ }
/// struct Actor<T> { states: Vec<T>, /* ... */ }
///
/// impl Reducer<Action> for State {
/// fn reduce(&mut self, action: Action) {
/// // ...
/// }
/// }
///
/// impl<T: Clone> Reactor<T> for Actor<T> {
/// type Error = std::convert::Infallible; // TODO: use `!` once it's stable.
/// fn react(&mut self, state: &T) -> Result<(), Self::Error> {
/// self.states.push(state.clone());
/// Ok(())
/// }
/// }
///
/// let state = Rc::new(State { /* ... */ });
/// let reactor = Actor { states: vec![], /* ... */ };
/// let mut store = Store::new(state, reactor);
///
/// store.dispatch(Action { /* ... */ }); // `state` is not cloned yet.
///
/// // `reactor` now holds a reference to `state`.
///
/// store.dispatch(Action { /* ... */ }); // `state` is cloned through `Rc::make_mut`.
/// ```
impl<A, T> Reducer<A> for Rc<T>
where
T: Reducer<A> + Clone + ?Sized,
{
fn reduce(&mut self, action: A) {
Rc::make_mut(self).reduce(action);
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockall::predicate::*;
use test_strategy::proptest;
#[proptest]
fn reduce(action: u8) {
let mut mock = MockReducer::new();
mock.expect_reduce()
.with(eq(action))
.once()
.return_const(());
let mut reducer = Rc::new(mock);
Reducer::reduce(&mut reducer, action);
}
#[proptest]
fn cow(action: u8) {
let mut mock = MockReducer::new();
mock.expect_reduce().never();
mock.expect_clone().once().returning(move || {
let mut mock = MockReducer::new();
mock.expect_reduce()
.with(eq(action))
.once()
.return_const(());
mock.expect_clone().never();
mock
});
let mut reducer = Rc::new(mock);
let other = reducer.clone();
Reducer::reduce(&mut reducer, action);
drop(other);
}
}