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
use std::ops::DerefMut;
use crate::Effects;
/// `Reducer`s are responsible for updating a `Store`’s state in response to its `Action`s.
pub trait Reducer {
/// All of the possible actions that can be used to modify state.
type Action;
/// Both unit tests and command line applications often need to return their `Store`’s final
/// state. Therefore `Store`’s [`into_inner`] method shuts down the `Store` and converts its
/// `Reducer` into its `Output` type.
///
/// Using a separate `Output` type, rather than returning the `Reducer` itself, allows the
/// `Store`s to support `Reducer` types that are not [`Send`].
///
/// - `Reducer`s that do not need to support [`into_inner`] should use declare
/// `type Output = Self;` as it is a simple, recognizable default.
/// - A `Reducer` that _is_ `Send` can also default to `type Output = Self;`.
/// - Otherwise, the `Reducer` will need to declare an `Output` type that _is_ `Send` and
/// that can be crated [`From`] the `Reducer`’s state.
///
/// ```rust
/// # use std::rc::Rc;
/// # use std::cell::Cell;
/// # use composable::{Effects, Reducer, Store};
/// # #[derive(Default)]
/// struct State {
/// n: Rc<Cell<usize>>, // Rc<Cell<…>> is not Send
/// };
///
/// enum Action { /* … */ }
///
/// impl Reducer for State {
/// type Action = Action;
/// type Output = usize; // but the usize itself _is_
///
/// fn reduce(&mut self, action: Self::Action, send: impl Effects<Self::Action>) { /**/ }
/// }
///
/// impl From<State> for usize {
/// fn from(value: State) -> Self {
/// Cell::into_inner(Rc::into_inner(value.n).unwrap_or_default())
/// }
/// }
/// # let store = Store::<State>::default();
/// ```
///
/// [`into_inner`]: crate::Store::into_inner
///
/// In short, you can use `type Output = Self;` until the compiler says that you can’t.
type Output;
/// Updates the `Reducer`’s state in response to the action received.
///
/// Additional `Action`s that need to be performed as a side-effect of an `Action` should be
/// [invoked][`crate::effects::Effects`] on `effects`.
#[doc = include_str!("README.md")]
fn reduce(&mut self, action: Self::Action, send: impl Effects<Self::Action>);
}
impl<T: Reducer> Reducer for Box<T> {
type Action = T::Action;
type Output = T::Output;
fn reduce(&mut self, action: Self::Action, send: impl Effects<Self::Action>) {
self.deref_mut().reduce(action, send)
}
}
impl<T: Reducer> Reducer for Option<T> {
type Action = T::Action;
type Output = Option<T::Output>;
fn reduce(&mut self, action: Self::Action, send: impl Effects<Self::Action>) {
if let Some(state) = self {
state.reduce(action, send)
}
}
}