1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
pub type Reducer<TState, TAction> = fn(&TState, TAction) -> TState;
pub type Subscriber<TState> = fn(&TState);
pub struct Store<TState, TAction> {
reducer: Reducer<TState, TAction>,
state: TState,
subscribers: Vec<Subscriber<TState>>,
}
impl<TState, TAction> Store<TState, TAction> {
pub fn new(reducer: Reducer<TState, TAction>, default_state: TState) -> Store<TState, TAction> {
Store::<TState, TAction> {
reducer,
state: default_state,
subscribers: Vec::new(),
}
}
pub fn dispatch(&mut self, action: TAction) {
self.state = (self.reducer)(&self.state, action);
self.subscribers.iter().for_each(|subscriber| subscriber(&self.state));
}
pub fn subscribe(&mut self, subscriber: Subscriber<TState>) {
self.subscribers.push(subscriber);
}
pub fn state(&self) -> &TState {
&self.state
}
}
#[cfg(test)]
mod tests {
use super::*;
enum TestActions {
INCREMENT(i32),
DECREMENT(i32),
}
fn test_reducer(state: &i32, action: TestActions) -> i32 {
match action {
TestActions::INCREMENT(x) => state + x,
TestActions::DECREMENT(x) => state - x,
}
}
#[test]
fn new_default_state_works() {
let store = Store::<i32, TestActions>::new(test_reducer, 1);
let expected = &1;
let actual = store.state();
assert_eq!(expected, actual)
}
#[test]
fn dispatch_increment_once() {
let mut store = Store::<i32, TestActions>::new(test_reducer, 1);
store.dispatch(TestActions::INCREMENT(1));
let expected = &2;
let actual = store.state();
assert_eq!(expected, actual)
}
#[test]
fn dispatch_decrement_once() {
let mut store = Store::<i32, TestActions>::new(test_reducer, 1);
store.dispatch(TestActions::DECREMENT(1));
let expected = &0;
let actual = store.state();
assert_eq!(expected, actual)
}
#[test]
fn dispatch_mixed() {
let mut store = Store::<i32, TestActions>::new(test_reducer, 0);
store.dispatch(TestActions::DECREMENT(12));
store.dispatch(TestActions::DECREMENT(31));
store.dispatch(TestActions::INCREMENT(15));
store.dispatch(TestActions::DECREMENT(78));
store.dispatch(TestActions::INCREMENT(12));
store.dispatch(TestActions::INCREMENT(14));
let expected = &-80;
let actual = store.state();
assert_eq!(expected, actual)
}
}