Skip to main content

rust_elm/
program.rs

1//! Elm `Program` — init, update, subscriptions (fn-pointer path).
2
3use crate::cmd::Cmd;
4use crate::reducer::Reducer;
5use crate::sub::Sub;
6
7type InitPair<S, M> = (S, Cmd<M>);
8
9/// Elm `Program` — init, update, subscriptions (fn-pointer path).
10pub 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
30/// Program backed by any [`Reducer`] value.
31pub 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}