1use crate::cmd::Cmd;
4use crate::reducer::Reducer;
5use crate::sub::Sub;
6
7type InitPair<S, M> = (S, Cmd<M>);
8
9pub struct Program<S, M> {
11 pub init: fn() -> InitPair<S, M>,
12 pub update: fn(&mut S, M) -> Cmd<M>,
13 pub subscriptions: fn(&S) -> Sub<M>,
14}
15
16impl<S, M> Program<S, M> {
17 pub fn new(
18 init: fn() -> InitPair<S, M>,
19 update: fn(&mut S, M) -> Cmd<M>,
20 subscriptions: fn(&S) -> Sub<M>,
21 ) -> Self {
22 Self {
23 init,
24 update,
25 subscriptions,
26 }
27 }
28}
29
30pub struct ReducerProgram<R: Reducer> {
32 pub reducer: R,
33 pub init: fn() -> InitPair<R::State, R::Action>,
34 pub subscriptions: fn(&R::State) -> Sub<R::Action>,
35}
36
37impl<R: Reducer> ReducerProgram<R> {
38 pub fn new(
39 reducer: R,
40 init: fn() -> InitPair<R::State, R::Action>,
41 subscriptions: fn(&R::State) -> Sub<R::Action>,
42 ) -> Self {
43 Self {
44 reducer,
45 init,
46 subscriptions,
47 }
48 }
49
50 pub fn from_parts(
51 reducer: R,
52 init: fn() -> InitPair<R::State, R::Action>,
53 subscriptions: fn(&R::State) -> Sub<R::Action>,
54 ) -> Self {
55 Self::new(reducer, init, subscriptions)
56 }
57}
58
59#[cfg(test)]
60#[allow(dead_code, clippy::type_complexity)]
61mod tests {
62 use super::*;
63 use crate::reducer::{coerce_fn, CombineReducers, Reduce};
64
65 #[derive(Default)]
66 struct Counter {
67 n: i32,
68 }
69
70 fn init() -> (Counter, Cmd<i32>) {
71 (Counter::default(), Cmd::none())
72 }
73
74 fn update(state: &mut Counter, msg: i32) -> Cmd<i32> {
75 state.n += msg;
76 Cmd::none()
77 }
78
79 fn subs(_: &Counter) -> Sub<i32> {
80 Sub::none()
81 }
82
83 #[test]
84 fn program_triple_wires() {
85 let program = Program::new(init, update, subs);
86 let (mut state, _) = (program.init)();
87 let _ = (program.update)(&mut state, 1);
88 assert_eq!(state.n, 1);
89 assert!(matches!((program.subscriptions)(&state), Sub::None));
90 }
91
92 #[test]
93 fn reducer_program_delegates_to_reducer() {
94 fn bump(state: &mut Counter, msg: i32) -> Cmd<i32> {
95 state.n += msg * 2;
96 Cmd::none()
97 }
98 let program = ReducerProgram::new(Reduce::new(bump), init, subs);
99 let (mut state, _) = (program.init)();
100 let _ = program.reducer.reduce(&mut state, 3);
101 assert_eq!(state.n, 6);
102 }
103
104 #[test]
105 fn reducer_program_accepts_combine() {
106 fn add_one(s: &mut Counter, m: i32) -> Cmd<i32> {
107 s.n += m;
108 Cmd::none()
109 }
110 fn add_ten(s: &mut Counter, m: i32) -> Cmd<i32> {
111 s.n += m * 10;
112 Cmd::none()
113 }
114 let program = ReducerProgram::new(
115 CombineReducers((coerce_fn(add_one), coerce_fn(add_ten))),
116 init,
117 subs,
118 );
119 let (mut state, _) = (program.init)();
120 program.reducer.reduce(&mut state, 2);
121 assert_eq!(state.n, 22);
122 }
123}