Skip to content

Commit 068039a

Browse files
committed
feat(dispatch-queue): Add dispatch queue and substate support
1 parent 9826c52 commit 068039a

File tree

6 files changed

+167
-7
lines changed

6 files changed

+167
-7
lines changed

src/action/enabling_condition.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,3 @@ pub trait EnablingCondition<State> {
77
true
88
}
99
}
10-

src/dispatcher.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use std::{collections::VecDeque, marker::PhantomData};
2+
3+
pub struct Dispatcher<Action, State> {
4+
queue: VecDeque<Action>,
5+
_marker: PhantomData<State>,
6+
}
7+
8+
impl<Action, State> Dispatcher<Action, State>
9+
where
10+
Action: crate::EnablingCondition<State>,
11+
{
12+
pub fn new() -> Self {
13+
Self {
14+
queue: VecDeque::new(),
15+
_marker: Default::default(),
16+
}
17+
}
18+
19+
pub fn push<T>(&mut self, action: T)
20+
where
21+
T: Into<Action>,
22+
{
23+
self.queue.push_back(action.into());
24+
}
25+
26+
pub(crate) fn pop(&mut self) -> Option<Action> {
27+
self.queue.pop_front()
28+
}
29+
30+
pub(crate) fn push_front(&mut self, other: Dispatcher<Action, State>) {
31+
other
32+
.queue
33+
.into_iter()
34+
.rev()
35+
.for_each(|action| self.queue.push_front(action));
36+
}
37+
}

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,9 @@ pub use store::{monotonic_to_time, Store};
2020

2121
mod sub_store;
2222
pub use sub_store::SubStore;
23+
24+
mod dispatcher;
25+
pub use dispatcher::Dispatcher;
26+
27+
mod substate;
28+
pub use substate::{Substate, SubstateAccess};

src/reducer.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use crate::ActionWithMeta;
1+
use crate::{ActionWithMeta, Dispatcher};
22

33
/// Function signature for a reducer.
4-
pub type Reducer<State, Action> = fn(&mut State, &ActionWithMeta<Action>);
4+
pub type Reducer<State, Action> =
5+
fn(&mut State, &ActionWithMeta<Action>, &mut Dispatcher<Action, State>);

src/store.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use std::sync::OnceLock;
22

33
use crate::{
4-
ActionId, ActionMeta, ActionWithMeta, Effects, EnablingCondition, Instant, Reducer, SubStore,
5-
SystemTime, TimeService,
4+
ActionId, ActionMeta, ActionWithMeta, Dispatcher, Effects, EnablingCondition, Instant, Reducer,
5+
SubStore, SystemTime, TimeService,
66
};
77

88
/// Wraps around State and allows only immutable borrow,
@@ -75,11 +75,15 @@ pub struct Store<State, Service, Action> {
7575
recursion_depth: u32,
7676

7777
last_action_id: ActionId,
78+
79+
/// Queue for actions to be dispatched after the state update
80+
dispatch_queue: Dispatcher<Action, State>,
7881
}
7982

8083
impl<State, Service, Action> Store<State, Service, Action>
8184
where
8285
Service: TimeService,
86+
Action: EnablingCondition<State>,
8387
{
8488
/// Creates a new store.
8589
pub fn new(
@@ -109,6 +113,8 @@ where
109113

110114
recursion_depth: 0,
111115
last_action_id: ActionId::new_unchecked(initial_time_nanos as u64),
116+
117+
dispatch_queue: Dispatcher::new(),
112118
}
113119
}
114120

@@ -186,6 +192,7 @@ where
186192
self.last_action_id = curr;
187193
self.recursion_depth += 1;
188194

195+
// TODO: instead return queued actions and pass them to dispatch_effects?
189196
self.dispatch_reducer(&action_with_meta);
190197
self.dispatch_effects(action_with_meta);
191198

@@ -195,21 +202,34 @@ where
195202
/// Runs the reducer.
196203
#[inline(always)]
197204
fn dispatch_reducer(&mut self, action_with_id: &ActionWithMeta<Action>) {
198-
(self.reducer)(self.state.get_mut(), action_with_id);
205+
// All new queued elements will be stored here
206+
let mut queue = Dispatcher::new();
207+
(self.reducer)(self.state.get_mut(), action_with_id, &mut queue);
208+
209+
// All the enqueued actions gets pushed to the front of the global queue
210+
self.dispatch_queue.push_front(queue);
199211
}
200212

201213
/// Runs the effects.
202214
#[inline(always)]
203215
fn dispatch_effects(&mut self, action_with_id: ActionWithMeta<Action>) {
216+
// First the effects for this specific action must be handled
204217
(self.effects)(self, action_with_id);
218+
219+
// Then dispatch all actions enqueued by the reducer
220+
while let Some(action) = self.dispatch_queue.pop() {
221+
if action.is_enabled(self.state(), self.last_action_id.into()) {
222+
self.dispatch_enabled(action);
223+
}
224+
}
205225
}
206226
}
207227

208228
impl<State, Service, Action> Clone for Store<State, Service, Action>
209229
where
210230
State: Clone,
211231
Service: Clone,
212-
Action: Clone,
232+
Action: Clone + EnablingCondition<State>,
213233
{
214234
fn clone(&self) -> Self {
215235
Self {
@@ -222,6 +242,8 @@ where
222242

223243
recursion_depth: self.recursion_depth,
224244
last_action_id: self.last_action_id,
245+
246+
dispatch_queue: Dispatcher::new(), // TODO: clone
225247
}
226248
}
227249
}

src/substate.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use crate::Dispatcher;
2+
3+
pub trait SubstateAccess<T> {
4+
fn substate(&self) -> &T;
5+
fn substate_mut(&mut self) -> &mut T;
6+
}
7+
8+
pub struct Substate<'a, A, T, S> {
9+
state: &'a mut T,
10+
dispatcher: &'a mut Dispatcher<A, T>,
11+
_marker: std::marker::PhantomData<S>,
12+
}
13+
14+
impl<'a, A, T, S> Substate<'a, A, T, S>
15+
where
16+
T: SubstateAccess<S>,
17+
{
18+
pub fn new(state: &'a mut T, dispatcher: &'a mut Dispatcher<A, T>) -> Self {
19+
Self {
20+
state,
21+
dispatcher,
22+
_marker: Default::default(),
23+
}
24+
}
25+
26+
pub fn from_compatible_substate<OS>(other: Substate<'a, A, T, OS>) -> Substate<'a, A, T, S> {
27+
let Substate {
28+
state, dispatcher, ..
29+
} = other;
30+
31+
Self::new(state, dispatcher)
32+
}
33+
34+
pub fn get_substate(&self) -> &S {
35+
self.state.substate()
36+
}
37+
38+
pub fn get_substate_mut(&mut self) -> &mut S {
39+
self.state.substate_mut()
40+
}
41+
42+
pub fn into_dispatcher_and_state(self) -> (&'a mut Dispatcher<A, T>, &'a T) {
43+
(self.dispatcher, self.state)
44+
}
45+
46+
pub fn into_dispatcher(self) -> &'a mut Dispatcher<A, T> {
47+
self.dispatcher
48+
}
49+
}
50+
51+
impl<'a, A, T, S> From<(&'a mut T, &'a mut Dispatcher<A, T>)> for Substate<'a, A, T, S> {
52+
fn from(state_and_dispatcher: (&'a mut T, &'a mut Dispatcher<A, T>)) -> Self {
53+
let (state, dispatcher) = state_and_dispatcher;
54+
Self {
55+
state,
56+
dispatcher,
57+
_marker: Default::default(),
58+
}
59+
}
60+
}
61+
62+
impl<'a, A, T, S> std::ops::Deref for Substate<'a, A, T, S>
63+
where
64+
T: SubstateAccess<S>,
65+
{
66+
type Target = S;
67+
68+
fn deref(&self) -> &Self::Target {
69+
self.get_substate()
70+
}
71+
}
72+
73+
impl<'a, A, T, S> std::ops::DerefMut for Substate<'a, A, T, S>
74+
where
75+
T: SubstateAccess<S>,
76+
{
77+
fn deref_mut(&mut self) -> &mut Self::Target {
78+
self.get_substate_mut()
79+
}
80+
}
81+
82+
#[macro_export]
83+
macro_rules! impl_substate_access {
84+
($state:ty, $substate_type:ty, $($substate_path:tt)*) => {
85+
impl redux::SubstateAccess<$substate_type> for $state {
86+
fn substate(&self) -> &$substate_type {
87+
&self.$($substate_path)*
88+
}
89+
90+
fn substate_mut(&mut self) -> &mut $substate_type {
91+
&mut self.$($substate_path)*
92+
}
93+
}
94+
};
95+
}

0 commit comments

Comments
 (0)