diff --git a/src/action/enabling_condition.rs b/src/action/enabling_condition.rs index fc30d5f..df75e1a 100644 --- a/src/action/enabling_condition.rs +++ b/src/action/enabling_condition.rs @@ -7,4 +7,3 @@ pub trait EnablingCondition { true } } - diff --git a/src/dispatcher.rs b/src/dispatcher.rs new file mode 100644 index 0000000..37be762 --- /dev/null +++ b/src/dispatcher.rs @@ -0,0 +1,37 @@ +use std::{collections::VecDeque, marker::PhantomData}; + +pub struct Dispatcher { + queue: VecDeque, + _marker: PhantomData, +} + +impl Dispatcher +where + Action: crate::EnablingCondition, +{ + pub fn new() -> Self { + Self { + queue: VecDeque::new(), + _marker: Default::default(), + } + } + + pub fn push(&mut self, action: T) + where + T: Into, + { + self.queue.push_back(action.into()); + } + + pub(crate) fn pop(&mut self) -> Option { + self.queue.pop_front() + } + + pub(crate) fn push_front(&mut self, other: Dispatcher) { + other + .queue + .into_iter() + .rev() + .for_each(|action| self.queue.push_front(action)); + } +} diff --git a/src/lib.rs b/src/lib.rs index d8cf41e..40dc46c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,3 +20,6 @@ pub use store::{monotonic_to_time, Store}; mod sub_store; pub use sub_store::SubStore; + +mod dispatcher; +pub use dispatcher::Dispatcher; diff --git a/src/reducer.rs b/src/reducer.rs index c3d6738..620e27c 100644 --- a/src/reducer.rs +++ b/src/reducer.rs @@ -1,4 +1,5 @@ -use crate::ActionWithMeta; +use crate::{ActionWithMeta, Dispatcher}; /// Function signature for a reducer. -pub type Reducer = fn(&mut State, &ActionWithMeta); +pub type Reducer = + fn(&mut State, &ActionWithMeta, &mut Dispatcher); diff --git a/src/store.rs b/src/store.rs index ff12cac..916646f 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,8 +1,8 @@ use std::sync::OnceLock; use crate::{ - ActionId, ActionMeta, ActionWithMeta, Effects, EnablingCondition, Instant, Reducer, SubStore, - SystemTime, TimeService, + ActionId, ActionMeta, ActionWithMeta, Dispatcher, Effects, EnablingCondition, Instant, Reducer, + SubStore, SystemTime, TimeService, }; /// Wraps around State and allows only immutable borrow, @@ -75,11 +75,15 @@ pub struct Store { recursion_depth: u32, last_action_id: ActionId, + + /// Queue for actions to be dispatched after the state update + dispatch_queue: Dispatcher, } impl Store where Service: TimeService, + Action: EnablingCondition, { /// Creates a new store. pub fn new( @@ -109,6 +113,8 @@ where recursion_depth: 0, last_action_id: ActionId::new_unchecked(initial_time_nanos as u64), + + dispatch_queue: Dispatcher::new(), } } @@ -186,6 +192,7 @@ where self.last_action_id = curr; self.recursion_depth += 1; + // TODO: instead return queued actions and pass them to dispatch_effects? self.dispatch_reducer(&action_with_meta); self.dispatch_effects(action_with_meta); @@ -195,13 +202,26 @@ where /// Runs the reducer. #[inline(always)] fn dispatch_reducer(&mut self, action_with_id: &ActionWithMeta) { - (self.reducer)(self.state.get_mut(), action_with_id); + // All new queued elements will be stored here + let mut queue = Dispatcher::new(); + (self.reducer)(self.state.get_mut(), action_with_id, &mut queue); + + // All the enqueued actions gets pushed to the front of the global queue + self.dispatch_queue.push_front(queue); } /// Runs the effects. #[inline(always)] fn dispatch_effects(&mut self, action_with_id: ActionWithMeta) { + // First the effects for this specific action must be handled (self.effects)(self, action_with_id); + + // Then dispatch all actions enqueued by the reducer + while let Some(action) = self.dispatch_queue.pop() { + if action.is_enabled(self.state(), self.last_action_id.into()) { + self.dispatch_enabled(action); + } + } } } @@ -209,7 +229,7 @@ impl Clone for Store where State: Clone, Service: Clone, - Action: Clone, + Action: Clone + EnablingCondition, { fn clone(&self) -> Self { Self { @@ -222,6 +242,8 @@ where recursion_depth: self.recursion_depth, last_action_id: self.last_action_id, + + dispatch_queue: Dispatcher::new(), // TODO: clone } } }