From 068039a8527cc8479bc1bcf6dfa98f03890e480c Mon Sep 17 00:00:00 2001 From: Bruno Deferrari Date: Sun, 3 Mar 2024 16:07:49 -0300 Subject: [PATCH 1/2] feat(dispatch-queue): Add dispatch queue and substate support --- src/action/enabling_condition.rs | 1 - src/dispatcher.rs | 37 +++++++++++++ src/lib.rs | 6 ++ src/reducer.rs | 5 +- src/store.rs | 30 ++++++++-- src/substate.rs | 95 ++++++++++++++++++++++++++++++++ 6 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 src/dispatcher.rs create mode 100644 src/substate.rs 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..6c52efc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,3 +20,9 @@ pub use store::{monotonic_to_time, Store}; mod sub_store; pub use sub_store::SubStore; + +mod dispatcher; +pub use dispatcher::Dispatcher; + +mod substate; +pub use substate::{Substate, SubstateAccess}; 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 } } } diff --git a/src/substate.rs b/src/substate.rs new file mode 100644 index 0000000..d155330 --- /dev/null +++ b/src/substate.rs @@ -0,0 +1,95 @@ +use crate::Dispatcher; + +pub trait SubstateAccess { + fn substate(&self) -> &T; + fn substate_mut(&mut self) -> &mut T; +} + +pub struct Substate<'a, A, T, S> { + state: &'a mut T, + dispatcher: &'a mut Dispatcher, + _marker: std::marker::PhantomData, +} + +impl<'a, A, T, S> Substate<'a, A, T, S> +where + T: SubstateAccess, +{ + pub fn new(state: &'a mut T, dispatcher: &'a mut Dispatcher) -> Self { + Self { + state, + dispatcher, + _marker: Default::default(), + } + } + + pub fn from_compatible_substate(other: Substate<'a, A, T, OS>) -> Substate<'a, A, T, S> { + let Substate { + state, dispatcher, .. + } = other; + + Self::new(state, dispatcher) + } + + pub fn get_substate(&self) -> &S { + self.state.substate() + } + + pub fn get_substate_mut(&mut self) -> &mut S { + self.state.substate_mut() + } + + pub fn into_dispatcher_and_state(self) -> (&'a mut Dispatcher, &'a T) { + (self.dispatcher, self.state) + } + + pub fn into_dispatcher(self) -> &'a mut Dispatcher { + self.dispatcher + } +} + +impl<'a, A, T, S> From<(&'a mut T, &'a mut Dispatcher)> for Substate<'a, A, T, S> { + fn from(state_and_dispatcher: (&'a mut T, &'a mut Dispatcher)) -> Self { + let (state, dispatcher) = state_and_dispatcher; + Self { + state, + dispatcher, + _marker: Default::default(), + } + } +} + +impl<'a, A, T, S> std::ops::Deref for Substate<'a, A, T, S> +where + T: SubstateAccess, +{ + type Target = S; + + fn deref(&self) -> &Self::Target { + self.get_substate() + } +} + +impl<'a, A, T, S> std::ops::DerefMut for Substate<'a, A, T, S> +where + T: SubstateAccess, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.get_substate_mut() + } +} + +#[macro_export] +macro_rules! impl_substate_access { + ($state:ty, $substate_type:ty, $($substate_path:tt)*) => { + impl redux::SubstateAccess<$substate_type> for $state { + fn substate(&self) -> &$substate_type { + &self.$($substate_path)* + } + + fn substate_mut(&mut self) -> &mut $substate_type { + &mut self.$($substate_path)* + } + } + }; +} From 84143eb682a2e39ba90089e739b093c865883c50 Mon Sep 17 00:00:00 2001 From: Bruno Deferrari Date: Fri, 19 Apr 2024 16:05:32 -0300 Subject: [PATCH 2/2] Remove substate --- src/lib.rs | 3 -- src/substate.rs | 95 ------------------------------------------------- 2 files changed, 98 deletions(-) delete mode 100644 src/substate.rs diff --git a/src/lib.rs b/src/lib.rs index 6c52efc..40dc46c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,3 @@ pub use sub_store::SubStore; mod dispatcher; pub use dispatcher::Dispatcher; - -mod substate; -pub use substate::{Substate, SubstateAccess}; diff --git a/src/substate.rs b/src/substate.rs deleted file mode 100644 index d155330..0000000 --- a/src/substate.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::Dispatcher; - -pub trait SubstateAccess { - fn substate(&self) -> &T; - fn substate_mut(&mut self) -> &mut T; -} - -pub struct Substate<'a, A, T, S> { - state: &'a mut T, - dispatcher: &'a mut Dispatcher, - _marker: std::marker::PhantomData, -} - -impl<'a, A, T, S> Substate<'a, A, T, S> -where - T: SubstateAccess, -{ - pub fn new(state: &'a mut T, dispatcher: &'a mut Dispatcher) -> Self { - Self { - state, - dispatcher, - _marker: Default::default(), - } - } - - pub fn from_compatible_substate(other: Substate<'a, A, T, OS>) -> Substate<'a, A, T, S> { - let Substate { - state, dispatcher, .. - } = other; - - Self::new(state, dispatcher) - } - - pub fn get_substate(&self) -> &S { - self.state.substate() - } - - pub fn get_substate_mut(&mut self) -> &mut S { - self.state.substate_mut() - } - - pub fn into_dispatcher_and_state(self) -> (&'a mut Dispatcher, &'a T) { - (self.dispatcher, self.state) - } - - pub fn into_dispatcher(self) -> &'a mut Dispatcher { - self.dispatcher - } -} - -impl<'a, A, T, S> From<(&'a mut T, &'a mut Dispatcher)> for Substate<'a, A, T, S> { - fn from(state_and_dispatcher: (&'a mut T, &'a mut Dispatcher)) -> Self { - let (state, dispatcher) = state_and_dispatcher; - Self { - state, - dispatcher, - _marker: Default::default(), - } - } -} - -impl<'a, A, T, S> std::ops::Deref for Substate<'a, A, T, S> -where - T: SubstateAccess, -{ - type Target = S; - - fn deref(&self) -> &Self::Target { - self.get_substate() - } -} - -impl<'a, A, T, S> std::ops::DerefMut for Substate<'a, A, T, S> -where - T: SubstateAccess, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - self.get_substate_mut() - } -} - -#[macro_export] -macro_rules! impl_substate_access { - ($state:ty, $substate_type:ty, $($substate_path:tt)*) => { - impl redux::SubstateAccess<$substate_type> for $state { - fn substate(&self) -> &$substate_type { - &self.$($substate_path)* - } - - fn substate_mut(&mut self) -> &mut $substate_type { - &mut self.$($substate_path)* - } - } - }; -}