Skip to main content

rust_elm/
program.rs

1use crate::cmd::Cmd;
2use crate::reducer::Reducer;
3use crate::sub::Sub;
4
5/// Elm `Program` — init, update, subscriptions (fn-pointer path).
6pub 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
26/// Program backed by any [`Reducer`] value.
27pub 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}