1use crate::{
2 middleware::{Middleware, ReduceMiddlewareResult},
3 AsListener, Listener, Reducer,
4};
5use std::iter::FromIterator;
6use std::ops::Deref;
7use std::{
8 cell::{Cell, RefCell},
9 collections::{HashSet, VecDeque},
10 fmt::Debug,
11 hash::Hash,
12 marker::PhantomData,
13 rc::Rc,
14};
15
16struct ListenerEventPair<State, Event> {
19 pub listener: Listener<State, Event>,
20 pub events: HashSet<Event>,
21}
22
23impl<State, Event> Debug for ListenerEventPair<State, Event> {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 write!(f, "ListenerEventPair")
26 }
27}
28
29enum StoreModification<State, Action, Event, Effect> {
33 AddListener(ListenerEventPair<State, Event>),
34 AddMiddleware(Rc<dyn Middleware<State, Action, Event, Effect>>),
35}
36
37pub struct StoreRef<State, Action, Event, Effect>(Rc<Store<State, Action, Event, Effect>>);
44
45impl<State, Action, Event, Effect> StoreRef<State, Action, Event, Effect>
46where
47 Event: Clone + Hash + Eq,
48{
49 pub fn new<R: Reducer<State, Action, Event, Effect> + 'static>(
50 reducer: R,
51 initial_state: State,
52 ) -> Self {
53 Self(Rc::new(Store::new(reducer, initial_state)))
54 }
55}
56
57impl<State, Action, Event, Effect> Clone for StoreRef<State, Action, Event, Effect> {
58 fn clone(&self) -> Self {
59 Self(Rc::clone(&self.0))
60 }
61}
62
63impl<State, Action, Event, Effect> Deref for StoreRef<State, Action, Event, Effect> {
64 type Target = Store<State, Action, Event, Effect>;
65
66 fn deref(&self) -> &Self::Target {
67 &*self.0
68 }
69}
70
71impl<State, Action, Event, Effect> PartialEq for StoreRef<State, Action, Event, Effect> {
72 fn eq(&self, other: &Self) -> bool {
73 Rc::ptr_eq(&self.0, &other.0)
74 }
75}
76
77pub struct Store<State, Action, Event, Effect> {
95 dispatch_lock: RefCell<()>,
97 dispatch_queue: RefCell<VecDeque<Action>>,
99 modification_queue: RefCell<VecDeque<StoreModification<State, Action, Event, Effect>>>,
102 reducer: Box<dyn Reducer<State, Action, Event, Effect>>,
106 state: RefCell<Rc<State>>,
108 listeners: RefCell<Vec<ListenerEventPair<State, Event>>>,
112 #[allow(clippy::type_complexity)]
114 middleware: RefCell<Vec<Rc<dyn Middleware<State, Action, Event, Effect>>>>,
115 prev_middleware: Cell<i32>,
119 phantom_action: PhantomData<Action>,
120 phantom_event: PhantomData<Event>,
121}
122
123impl<State, Action, Event, Effect> Store<State, Action, Event, Effect>
124where
125 Event: Clone + Hash + Eq,
126{
127 pub fn new<R: Reducer<State, Action, Event, Effect> + 'static>(
131 reducer: R,
132 initial_state: State,
133 ) -> Self {
134 Self {
135 dispatch_lock: RefCell::new(()),
136 dispatch_queue: RefCell::new(VecDeque::new()),
137 modification_queue: RefCell::new(VecDeque::new()),
138 reducer: Box::new(reducer),
139 state: RefCell::new(Rc::new(initial_state)),
140 listeners: RefCell::new(Vec::new()),
141 middleware: RefCell::new(Vec::new()),
142 prev_middleware: Cell::new(-1),
143 phantom_action: PhantomData,
144 phantom_event: PhantomData,
145 }
146 }
147
148 pub fn state(&self) -> Rc<State> {
154 self.state.borrow().clone()
155 }
156
157 fn dispatch_reducer(&self, action: &Action) -> ReduceMiddlewareResult<Event, Effect> {
160 let result = self.reducer.reduce(&self.state(), action);
161 *self.state.borrow_mut() = result.state;
162
163 ReduceMiddlewareResult {
164 events: result.events,
165 effects: result.effects,
166 }
167 }
168
169 fn middleware_reduce(&self, action: &Action) -> ReduceMiddlewareResult<Event, Effect> {
172 self.prev_middleware.set(-1);
173 self.middleware_reduce_next(Some(action))
174 }
175
176 fn middleware_reduce_next(
181 &self,
182 action: Option<&Action>,
183 ) -> ReduceMiddlewareResult<Event, Effect> {
184 let current_middleware = self.prev_middleware.get() + 1;
185 self.prev_middleware.set(current_middleware);
186
187 if current_middleware == self.middleware.borrow().len() as i32 {
188 return match action {
189 Some(action) => self.dispatch_reducer(action),
190 None => ReduceMiddlewareResult::default(),
191 };
192 }
193
194 self.middleware.borrow()[current_middleware as usize]
195 .clone()
196 .on_reduce(self, action, Self::middleware_reduce_next)
197 }
198
199 fn middleware_process_effects(&self, effects: Vec<Effect>) {
203 for effect in effects {
204 self.middleware_process_effect(effect);
205 }
206 }
207
208 fn middleware_process_effect(&self, effect: Effect) {
212 self.prev_middleware.set(-1);
213 self.middleware_process_effects_next(effect);
214 }
215
216 fn middleware_process_effects_next(&self, effect: Effect) {
221 let current_middleware = self.prev_middleware.get() + 1;
222 self.prev_middleware.set(current_middleware);
223
224 if current_middleware == self.middleware.borrow().len() as i32 {
225 return;
226 }
227
228 if let Some(effect) = self.middleware.borrow()[current_middleware as usize]
229 .clone()
230 .process_effect(self, effect)
231 {
232 self.middleware_process_effects_next(effect);
233 }
234 }
235
236 fn middleware_notify(&self, events: Vec<Event>) -> Vec<Event> {
241 self.prev_middleware.set(-1);
242 self.middleware_notify_next(events)
243 }
244
245 fn middleware_notify_next(&self, events: Vec<Event>) -> Vec<Event> {
252 let current_middleware = self.prev_middleware.get() + 1;
253 self.prev_middleware.set(current_middleware);
254
255 if current_middleware == self.middleware.borrow().len() as i32 {
256 return events;
257 }
258
259 self.middleware.borrow()[current_middleware as usize]
260 .clone()
261 .on_notify(self, events, Self::middleware_notify_next)
262 }
263
264 fn notify_listeners(&self, events: Vec<Event>) {
268 let mut listeners_to_remove: Vec<usize> = Vec::new();
269 for (i, pair) in self.listeners.borrow().iter().enumerate() {
270 let retain = match pair.listener.as_callback() {
271 Some(callback) => {
272 if pair.events.is_empty() {
273 callback.emit(self.state.borrow().clone(), None);
274 } else {
275 for event in &events {
277 if pair.events.contains(event) {
278 callback.emit(self.state.borrow().clone(), Some(event.clone()));
279 }
280 }
281 }
282
283 true
284 }
285 None => false,
286 };
287
288 if !retain {
289 listeners_to_remove.insert(0, i);
290 }
291 }
292
293 for index in listeners_to_remove {
294 self.listeners.borrow_mut().swap_remove(index);
295 }
296 }
297
298 fn process_pending_modifications(&self) {
299 while let Some(modification) = self.modification_queue.borrow_mut().pop_front() {
300 match modification {
301 StoreModification::AddListener(listener_pair) => {
302 self.listeners.borrow_mut().push(listener_pair);
303 }
304 StoreModification::AddMiddleware(middleware) => {
305 self.middleware.borrow_mut().push(middleware);
306 }
307 }
308 }
309 }
310
311 pub fn dispatch<A: Into<Action>>(&self, action: A) {
315 self.dispatch_impl(action.into());
316 }
317
318 fn dispatch_impl(&self, action: Action) {
324 self.dispatch_queue.borrow_mut().push_back(action);
325
326 if let Ok(_lock) = self.dispatch_lock.try_borrow_mut() {
330 loop {
334 let dispatch_action = self.dispatch_queue.borrow_mut().pop_front();
335
336 match dispatch_action {
337 Some(action) => {
338 self.process_pending_modifications();
339
340 let reduce_middleware_result = if self.middleware.borrow().is_empty() {
341 self.dispatch_reducer(&action)
342 } else {
343 self.middleware_reduce(&action)
344 };
345
346 #[allow(clippy::match_single_binding)] match reduce_middleware_result {
348 ReduceMiddlewareResult { events, effects } => {
349 self.middleware_process_effects(effects);
350
351 let middleware_events = self.middleware_notify(events);
352 if !middleware_events.is_empty() {
353 self.notify_listeners(middleware_events);
354 }
355 }
356 }
357 }
358 None => {
359 break;
360 }
361 }
362 }
363 }
364 }
365
366 pub fn subscribe<L: AsListener<State, Event>>(&self, listener: L) {
380 self.modification_queue
381 .borrow_mut()
382 .push_back(StoreModification::AddListener(ListenerEventPair {
383 listener: listener.as_listener(),
384 events: HashSet::new(),
385 }));
386 }
387
388 pub fn subscribe_event<L: AsListener<State, Event>>(&self, listener: L, event: Event) {
400 let mut events = HashSet::with_capacity(1);
401 events.insert(event);
402
403 self.modification_queue
404 .borrow_mut()
405 .push_back(StoreModification::AddListener(ListenerEventPair {
406 listener: listener.as_listener(),
407 events,
408 }));
409 }
410
411 pub fn subscribe_events<L: AsListener<State, Event>, E: IntoIterator<Item = Event>>(
423 &self,
424 listener: L,
425 events: E,
426 ) {
427 self.modification_queue
428 .borrow_mut()
429 .push_back(StoreModification::AddListener(ListenerEventPair {
430 listener: listener.as_listener(),
431 events: HashSet::from_iter(events.into_iter()),
432 }));
433 }
434
435 pub fn add_middleware<M: Middleware<State, Action, Event, Effect> + 'static>(
438 &self,
439 middleware: M,
440 ) {
441 self.modification_queue
442 .borrow_mut()
443 .push_back(StoreModification::AddMiddleware(Rc::new(middleware)));
444 }
445}
446
447#[cfg(test)]
448mod tests {
449 use crate::{
450 middleware::{Middleware, ReduceMiddlewareResult},
451 Callback, Reducer, ReducerResult, Store, StoreRef,
452 };
453 use std::{cell::RefCell, rc::Rc};
454
455 #[derive(Debug, PartialEq)]
456 struct TestState {
457 counter: i32,
458 }
459
460 #[derive(Copy, Clone)]
461 enum TestAction {
462 Increment,
463 Decrement,
464 Decrement2,
465 Decrent2Then1,
466 NoEvent,
467 }
468
469 enum TestEffect {
470 ChainAction(TestAction),
471 }
472
473 struct TestReducer;
474
475 impl Reducer<TestState, TestAction, TestEvent, TestEffect> for TestReducer {
476 fn reduce(
477 &self,
478 state: &Rc<TestState>,
479 action: &TestAction,
480 ) -> ReducerResult<TestState, TestEvent, TestEffect> {
481 let mut events = Vec::new();
482 let mut effects = Vec::new();
483
484 let new_state = match action {
485 TestAction::Increment => {
486 events.push(TestEvent::CounterChanged);
487 TestState {
488 counter: state.counter + 1,
489 }
490 }
491 TestAction::Decrement => {
492 events.push(TestEvent::CounterChanged);
493 TestState {
494 counter: state.counter - 1,
495 }
496 }
497 TestAction::Decrement2 => {
498 events.push(TestEvent::CounterChanged);
499 TestState {
500 counter: state.counter - 2,
501 }
502 }
503 TestAction::Decrent2Then1 => {
504 effects.push(TestEffect::ChainAction(TestAction::Decrement));
505 events.push(TestEvent::CounterChanged);
506
507 TestState {
508 counter: state.counter - 2,
509 }
510 }
511 TestAction::NoEvent => TestState { counter: 42 },
512 };
513
514 if new_state.counter != state.counter && new_state.counter == 0 {
515 events.push(TestEvent::CounterIsZero);
516 }
517
518 ReducerResult {
519 state: Rc::new(new_state),
520 events,
521 effects,
522 }
523 }
524 }
525
526 struct TestReduceMiddleware {
527 new_action: TestAction,
528 }
529
530 impl Middleware<TestState, TestAction, TestEvent, TestEffect> for TestReduceMiddleware {
531 fn on_reduce(
532 &self,
533 store: &Store<TestState, TestAction, TestEvent, TestEffect>,
534 action: Option<&TestAction>,
535 reduce: crate::middleware::ReduceFn<TestState, TestAction, TestEvent, TestEffect>,
536 ) -> ReduceMiddlewareResult<TestEvent, TestEffect> {
537 reduce(store, action.map(|_| &self.new_action))
538 }
539 }
540
541 struct TestEffectMiddleware;
542
543 impl Middleware<TestState, TestAction, TestEvent, TestEffect> for TestEffectMiddleware {
544 fn process_effect(
545 &self,
546 store: &Store<TestState, TestAction, TestEvent, TestEffect>,
547 effect: TestEffect,
548 ) -> Option<TestEffect> {
549 match effect {
550 TestEffect::ChainAction(action) => {
551 store.dispatch(action);
552 }
553 }
554
555 None
556 }
557 }
558
559 #[derive(Debug, PartialEq, Eq, Hash, Clone)]
560 enum TestEvent {
561 CounterIsZero,
562 CounterChanged,
563 }
564
565 #[test]
566 fn test_notify() {
567 let initial_state = TestState { counter: 0 };
568 let store: Rc<RefCell<Store<TestState, TestAction, TestEvent, TestEffect>>> =
569 Rc::new(RefCell::new(Store::new(TestReducer, initial_state)));
570
571 let callback_test = Rc::new(RefCell::new(0));
572 let callback_test_copy = callback_test.clone();
573 let callback: Callback<TestState, TestEvent> =
574 Callback::new(move |state: Rc<TestState>, _| {
575 *callback_test_copy.borrow_mut() = state.counter;
576 });
577
578 store.borrow_mut().subscribe(&callback);
579
580 assert_eq!(0, store.borrow().state().counter);
581
582 store.borrow_mut().dispatch(TestAction::Increment);
583 store.borrow_mut().dispatch(TestAction::Increment);
584 assert_eq!(2, *callback_test.borrow());
585 assert_eq!(2, store.borrow().state().counter);
586
587 store.borrow_mut().dispatch(TestAction::Decrement);
588 assert_eq!(1, store.borrow().state().counter);
589 }
590
591 #[test]
592 fn test_reduce_middleware() {
593 let initial_state = TestState { counter: 0 };
594 let store = StoreRef::new(TestReducer, initial_state);
595
596 let callback_test = Rc::new(RefCell::new(0));
597 let callback_test_copy = callback_test.clone();
598 let callback: Callback<TestState, TestEvent> =
599 Callback::new(move |state: Rc<TestState>, _| {
600 *callback_test_copy.borrow_mut() = state.counter;
601 });
602
603 store.subscribe(&callback);
604 store.add_middleware(TestReduceMiddleware {
605 new_action: TestAction::Decrement,
606 });
607 store.add_middleware(TestReduceMiddleware {
608 new_action: TestAction::Decrement2,
609 });
610
611 store.dispatch(TestAction::Increment);
612 assert_eq!(-2, *callback_test.borrow());
613 }
614
615 #[test]
616 fn test_reduce_middleware_reverse_order() {
617 let initial_state = TestState { counter: 0 };
618 let store = StoreRef::new(TestReducer, initial_state);
619
620 let callback_test = Rc::new(RefCell::new(0));
621 let callback_test_copy = callback_test.clone();
622 let callback: Callback<TestState, TestEvent> =
623 Callback::new(move |state: Rc<TestState>, _| {
624 *callback_test_copy.borrow_mut() = state.counter;
625 });
626
627 store.subscribe(&callback);
628 store.add_middleware(TestReduceMiddleware {
629 new_action: TestAction::Decrement2,
630 });
631 store.add_middleware(TestReduceMiddleware {
632 new_action: TestAction::Decrement,
633 });
634
635 store.dispatch(TestAction::Increment);
636 assert_eq!(-1, *callback_test.borrow());
637 }
638
639 #[test]
640 fn test_effect_middleware() {
641 let initial_state = TestState { counter: 0 };
642 let store = StoreRef::new(TestReducer, initial_state);
643 store.add_middleware(TestEffectMiddleware);
644
645 assert_eq!(store.state().counter, 0);
646 store.dispatch(TestAction::Decrent2Then1);
647 assert_eq!(store.state().counter, -3);
648 }
649
650 #[test]
651 fn test_subscribe_event() {
652 let initial_state = TestState { counter: -2 };
653 let store = StoreRef::new(TestReducer, initial_state);
654
655 let callback_test: Rc<RefCell<Option<TestEvent>>> = Rc::new(RefCell::new(None));
656 let callback_test_copy = callback_test.clone();
657
658 let callback_zero_subscription: Callback<TestState, TestEvent> =
659 Callback::new(move |_: Rc<TestState>, event| {
660 assert_eq!(Some(TestEvent::CounterIsZero), event);
661 *callback_test_copy.borrow_mut() = Some(TestEvent::CounterIsZero);
662 });
663
664 store.subscribe_event(&callback_zero_subscription, TestEvent::CounterIsZero);
665 store.dispatch(TestAction::Increment);
666 assert_eq!(None, *callback_test.borrow());
667 store.dispatch(TestAction::Increment);
668 assert_eq!(Some(TestEvent::CounterIsZero), *callback_test.borrow());
669 }
670
671 #[test]
673 fn test_subscribe_no_event() {
674 let initial_state = TestState { counter: 0 };
675 let store = StoreRef::new(TestReducer, initial_state);
676
677 let callback_test: Rc<RefCell<i32>> = Rc::new(RefCell::new(0));
678 let callback_test_copy = callback_test.clone();
679
680 let callback: Callback<TestState, TestEvent> =
681 Callback::new(move |state: Rc<TestState>, _event| {
682 assert_eq!(42, state.counter);
683 *callback_test_copy.borrow_mut() = state.counter;
684 });
685
686 store.subscribe(&callback);
687
688 assert_eq!(0, store.state.borrow().counter);
689 assert_eq!(0, *callback_test.borrow());
690
691 store.dispatch(TestAction::NoEvent);
692
693 assert_eq!(42, store.state.borrow().counter);
694
695 assert_eq!(0, *callback_test.borrow());
698 }
699}