Skip to main content

putzen_cli/caches/tui/
command.rs

1//! Pure aggregator returned by `update`. Reimplements the pattern of
2//! `crux_core::command::Command<Effect, Event>` with no external dependency.
3//!
4//! `events` are synchronous Msgs to feed back into `update` in the same tick.
5//! `effects` are IO descriptions handed to the runtime's effect runner.
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct Command<E, M> {
9    pub events: Vec<M>,
10    pub effects: Vec<E>,
11}
12
13impl<E, M> Command<E, M> {
14    pub fn done() -> Self {
15        Self {
16            events: Vec::new(),
17            effects: Vec::new(),
18        }
19    }
20
21    pub fn event(m: M) -> Self {
22        Self {
23            events: vec![m],
24            effects: Vec::new(),
25        }
26    }
27
28    pub fn effect(e: E) -> Self {
29        Self {
30            events: Vec::new(),
31            effects: vec![e],
32        }
33    }
34
35    pub fn batch<I: IntoIterator<Item = Self>>(cmds: I) -> Self {
36        let mut out = Self::done();
37        for c in cmds {
38            out.events.extend(c.events);
39            out.effects.extend(c.effects);
40        }
41        out
42    }
43
44    pub fn and(mut self, other: Self) -> Self {
45        self.events.extend(other.events);
46        self.effects.extend(other.effects);
47        self
48    }
49
50    pub fn is_done(&self) -> bool {
51        self.events.is_empty() && self.effects.is_empty()
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[derive(Debug, PartialEq, Eq, Clone)]
60    enum E {
61        A,
62        B,
63    }
64    #[derive(Debug, PartialEq, Eq, Clone)]
65    enum M {
66        X,
67        Y,
68    }
69
70    #[test]
71    fn done_is_empty() {
72        let c: Command<E, M> = Command::done();
73        assert!(c.is_done());
74    }
75
76    #[test]
77    fn event_carries_only_msg() {
78        let c: Command<E, M> = Command::event(M::X);
79        assert_eq!(c.events, vec![M::X]);
80        assert!(c.effects.is_empty());
81        assert!(!c.is_done());
82    }
83
84    #[test]
85    fn effect_carries_only_effect() {
86        let c: Command<E, M> = Command::effect(E::A);
87        assert!(c.events.is_empty());
88        assert_eq!(c.effects, vec![E::A]);
89    }
90
91    #[test]
92    fn and_concatenates_in_order() {
93        let c: Command<E, M> = Command::event(M::X).and(Command::effect(E::A));
94        assert_eq!(c.events, vec![M::X]);
95        assert_eq!(c.effects, vec![E::A]);
96
97        let d: Command<E, M> = Command::effect(E::B).and(Command::event(M::Y));
98        assert_eq!(d.events, vec![M::Y]);
99        assert_eq!(d.effects, vec![E::B]);
100    }
101
102    #[test]
103    fn batch_concatenates_all() {
104        let c: Command<E, M> = Command::batch([
105            Command::event(M::X),
106            Command::effect(E::A),
107            Command::event(M::Y),
108            Command::effect(E::B),
109        ]);
110        assert_eq!(c.events, vec![M::X, M::Y]);
111        assert_eq!(c.effects, vec![E::A, E::B]);
112    }
113
114    #[test]
115    fn batch_of_empty_is_done() {
116        let c: Command<E, M> = Command::batch(std::iter::empty());
117        assert!(c.is_done());
118    }
119}