rust_redux/
lib.rs

1use std::sync::{Arc, Mutex};
2
3#[derive(Clone)]
4pub struct Store<S, A>
5where
6    S: Clone,
7    A: Clone,
8{
9    state: Arc<Mutex<S>>,
10    reducer: fn(&S, &A) -> S,
11    subscribers: Arc<Mutex<Vec<Box<dyn Fn(&S) + Send + Sync>>>>,
12    middlewares: Vec<Arc<dyn Fn(&Store<S, A>, &A, &dyn Fn(&A)) + Send + Sync>>,
13}
14
15impl<S, A> Store<S, A>
16where
17    S: Clone + Send + Sync + 'static,
18    A: Clone + Send + Sync + 'static,
19{
20    pub fn new(reducer: fn(&S, &A) -> S, initial_state: S) -> Self {
21        Store {
22            state: Arc::new(Mutex::new(initial_state)),
23            reducer,
24            subscribers: Arc::new(Mutex::new(Vec::new())),
25            middlewares: Vec::new(),
26        }
27    }
28
29    pub fn add_middleware(&mut self, middleware: Arc<dyn Fn(&Store<S, A>, &A, &dyn Fn(&A)) + Send + Sync>) {
30        self.middlewares.push(middleware);
31    }
32
33    pub fn dispatch(&self, action: &A) {
34        let base_dispatch = |action: &A| {
35            let mut state = self.state.lock().unwrap();
36            *state = (self.reducer)(&state, action);
37            self.notify_subscribers(&state);
38        };
39
40        let dispatch = self.middlewares.iter().rev().fold(
41            Box::new(base_dispatch) as Box<dyn Fn(&A)>,
42            |next, middleware| {
43                Box::new(move |action: &A| {
44                    middleware(self, action, &|a| next(a));
45                }) as Box<dyn Fn(&A)>
46            },
47        );
48
49        dispatch(action);
50    }
51
52    pub fn subscribe<F>(&self, listener: F)
53    where
54        F: Fn(&S) + 'static + Send + Sync,
55    {
56        let mut subscribers = self.subscribers.lock().unwrap();
57        subscribers.push(Box::new(listener));
58    }
59
60    fn notify_subscribers(&self, state: &S) {
61        let subscribers = self.subscribers.lock().unwrap();
62        for listener in subscribers.iter() {
63            listener(state);
64        }
65    }
66
67    pub fn get_state(&self) -> S {
68        self.state.lock().unwrap().clone()
69    }
70
71    pub fn with_middleware(mut self, middlewares: Vec<Arc<dyn Fn(&Store<S, A>, &A, &dyn Fn(&A)) + Send + Sync>>) -> Self {
72        self.middlewares = middlewares;
73        self
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[derive(Clone, PartialEq, Debug)]
82    struct TestState {
83        pub counter: i32,
84    }
85
86    #[derive(Clone, Debug)]
87    enum TestAction {
88        Increment,
89        Decrement,
90    }
91
92    fn test_reducer(state: &TestState, action: &TestAction) -> TestState {
93        match action {
94            TestAction::Increment => TestState {
95                counter: state.counter + 1,
96            },
97            TestAction::Decrement => TestState {
98                counter: state.counter - 1,
99            },
100        }
101    }
102
103    fn logger_middleware<S, A>(
104        store: &Store<S, A>,
105        action: &A,
106        next: &dyn Fn(&A),
107    ) where
108        S: Clone + Send + Sync + 'static + std::fmt::Debug,
109        A: Clone + Send + Sync + 'static + std::fmt::Debug,
110    {
111        println!("Dispatching action: {:?}", action);
112        next(action);
113        println!("New state: {:?}", store.get_state());
114    }
115
116    #[test]
117    fn test_store() {
118        let initial_state = TestState { counter: 0 };
119        let store = Store::new(test_reducer as fn(&TestState, &TestAction) -> TestState, initial_state)
120            .with_middleware(vec![Arc::new(logger_middleware)]);
121
122        store.dispatch(&TestAction::Increment);
123        assert_eq!(store.get_state().counter, 1);
124
125        store.dispatch(&TestAction::Decrement);
126        assert_eq!(store.get_state().counter, 0);
127    }
128
129    #[test]
130    fn test_subscribe() {
131        let initial_state = TestState { counter: 0 };
132        let store = Store::new(test_reducer as fn(&TestState, &TestAction) -> TestState, initial_state)
133            .with_middleware(vec![Arc::new(logger_middleware)]);
134
135        let observed_states = Arc::new(Mutex::new(Vec::new()));
136        let observed_states_clone = Arc::clone(&observed_states);
137        store.subscribe(move |state: &TestState| {
138            let mut states = observed_states_clone.lock().unwrap();
139            states.push(state.counter);
140        });
141
142        store.dispatch(&TestAction::Increment);
143        store.dispatch(&TestAction::Increment);
144        store.dispatch(&TestAction::Decrement);
145
146        let observed_states = observed_states.lock().unwrap();
147        assert_eq!(*observed_states, vec![1, 2, 1]);
148    }
149}