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