use crate::dispatcher::Dispatcher;
use crate::reactor::Reactor;
use crate::reducer::Reducer;
use core::{mem, ops::Deref};
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Store<S, R: Reactor<S>> {
state: S,
reactor: R,
}
impl<S, R: Reactor<S>> Store<S, R> {
pub fn new(state: S, reactor: R) -> Self {
Self { state, reactor }
}
pub fn subscribe(&mut self, reactor: impl Into<R>) -> R {
mem::replace(&mut self.reactor, reactor.into())
}
}
impl<S, R: Reactor<S>> Deref for Store<S, R> {
type Target = S;
fn deref(&self) -> &Self::Target {
&self.state
}
}
impl<A, S, R> Dispatcher<A> for Store<S, R>
where
S: Reducer<A>,
R: Reactor<S>,
{
type Output = Result<(), R::Error>;
fn dispatch(&mut self, action: A) -> Self::Output {
self.state.reduce(action);
self.reactor.react(&self.state)
}
}
#[cfg(feature = "async")]
mod sink {
use super::*;
use futures::sink::Sink;
use futures::task::{Context, Poll};
use pin_utils::unsafe_pinned;
use std::pin::Pin;
impl<S, R: Reactor<S>> Store<S, R> {
unsafe_pinned!(state: S);
unsafe_pinned!(reactor: R);
}
impl<S: Unpin, R: Reactor<S> + Unpin> Unpin for Store<S, R> {}
impl<A, S, R, E> Sink<A> for Store<S, R>
where
S: Reducer<A> + Unpin + Clone,
R: Reactor<S, Error = E> + Sink<S, SinkError = E>,
{
type SinkError = E;
fn poll_ready(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::SinkError>> {
self.reactor().poll_ready(cx)
}
fn start_send(mut self: Pin<&mut Self>, action: A) -> Result<(), Self::SinkError> {
let state = {
let state: &mut S = self.as_mut().state().get_mut();
state.reduce(action);
state.clone()
};
self.reactor().start_send(state)
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::SinkError>> {
self.reactor().poll_flush(cx)
}
fn poll_close(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::SinkError>> {
self.reactor().poll_close(cx)
}
}
}
#[cfg(feature = "async")]
pub use sink::*;
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::*;
use proptest::prelude::*;
#[cfg(feature = "async")]
use futures::{executor::block_on, sink::SinkExt};
#[test]
fn default() {
let store = Store::<Mock<()>, Mock<_>>::default();
assert_eq!(store.state, Mock::default());
assert_eq!(store.reactor, Mock::default());
}
proptest! {
#[test]
fn new(state: Mock<()>, reactor: Mock<_>) {
let store = Store::new(state.clone(), reactor.clone());
assert_eq!(store.state, state);
assert_eq!(store.reactor, reactor);
}
}
proptest! {
#[test]
fn clone(state: Mock<()>, reactor: Mock<_>) {
let store = Store::new(state, reactor);
assert_eq!(store, store.clone());
}
}
proptest! {
#[test]
fn deref(state: Mock<()>, reactor: Mock<_>) {
let store = Store::new(state, reactor);
assert_eq!(*store, store.state);
}
}
proptest! {
#[test]
fn subscribe(state: Mock<()>, [r1, r2, r3]: [Mock<_>; 3]) {
let mut store = Store::new(state.clone(), r1.clone());
assert_eq!(store.state, state);
assert_eq!(store.reactor, r1);
assert_eq!(store.subscribe(r2.clone()), r1);
assert_eq!(store.state, state);
assert_eq!(store.reactor, r2);
assert_eq!(store.subscribe(r3.clone()), r2);
assert_eq!(store.state, state);
assert_eq!(store.reactor, r3);
}
}
proptest! {
#[test]
fn dispatcher(actions: Vec<u8>) {
let mut store = Store::<Mock<_>, Mock<_>>::default();
for (i, &action) in actions.iter().enumerate() {
assert_eq!(dispatch(&mut store, action), Ok(()));
assert_eq!(store.state.calls(), &actions[0..=i]);
assert_eq!(store.reactor.calls().last(), Some(&store.state));
}
}
}
proptest! {
#[cfg(feature = "async")]
#[test]
fn sink(actions: Vec<u8>) {
let mut store = Store::<Mock<_>, Mock<_>>::default();
for (i, &action) in actions.iter().enumerate() {
drop(store.send(action));
assert_eq!(store.state.calls(), &actions[0..i]);
assert_eq!(store.reactor.calls().len(), i);
assert_eq!(block_on(store.send(action)), Ok(()));
assert_eq!(store.state.calls(), &actions[0..=i]);
assert_eq!(store.reactor.calls().last(), Some(&store.state));
}
}
}
proptest! {
#[test]
fn error(state: Mock<_>, mut reactor: Mock<_, _>, error: String) {
let mut next = state.clone();
reduce(&mut next, ());
reactor.fail_if(next, &error[..]);
let mut store = Store::new(state, reactor);
assert_eq!(dispatch(&mut store, ()), Err(&error[..]));
}
}
}