sauron_core/dom/
dispatch.rs

1//! provides functionalities for commands to be executed by the system, such as
2//! when the application starts or after the application updates.
3//!
4use crate::dom::Program;
5use crate::dom::{Application, Cmd, Effects};
6use wasm_bindgen_futures::spawn_local;
7
8/// Dispatch is a command to be executed by the system.
9/// This is returned at the init function of a component and is executed right
10/// after instantiation of that component.
11/// Dispatch required a DSP object which is the Program as an argument
12/// The emit function is called with the program argument.
13/// The callback is supplied with the program an is then executed/emitted.
14pub struct Dispatch<APP>
15where
16    APP: Application,
17{
18    /// the functions that would be executed when this Dispatch is emited
19    #[allow(clippy::type_complexity)]
20    pub(crate) commands: Vec<Box<dyn FnOnce(Program<APP>)>>,
21}
22
23impl<APP> Dispatch<APP>
24where
25    APP: Application,
26{
27    /// creates a new Dispatch from a function
28    pub fn new<F>(f: F) -> Self
29    where
30        F: FnOnce(Program<APP>) + 'static,
31    {
32        Self {
33            commands: vec![Box::new(f)],
34        }
35    }
36
37    /// When you need the runtime to perform couple of commands, you can batch
38    /// then together.
39    pub fn batch(cmds: impl IntoIterator<Item = Self>) -> Self {
40        let mut commands = vec![];
41        for cmd in cmds {
42            commands.extend(cmd.commands);
43        }
44        Self { commands }
45    }
46
47    /// Add a cmd
48    pub fn push(&mut self, cmd: Self) {
49        self.append([cmd])
50    }
51
52    /// Append more cmd into this cmd and return self
53    pub fn append(&mut self, cmds: impl IntoIterator<Item = Self>) {
54        for cmd in cmds {
55            self.commands.extend(cmd.commands);
56        }
57    }
58
59    /// Tell the runtime that there are no commands.
60    pub fn none() -> Self {
61        Dispatch { commands: vec![] }
62    }
63
64    /// returns true if commands is empty
65    pub fn is_empty(&self) -> bool {
66        self.commands.is_empty()
67    }
68
69    /// Executes the Dispatch
70    pub(crate) fn emit(self, program: Program<APP>) {
71        for cb in self.commands {
72            let program_clone = program.clone();
73            cb(program_clone);
74        }
75    }
76
77    /// Tell the runtime to execute subsequent update of the App with the message list.
78    /// A single call to update the view is then executed thereafter.
79    ///
80    pub fn batch_msg(msg_list: impl IntoIterator<Item = APP::MSG>) -> Self {
81        let msg_list: Vec<APP::MSG> = msg_list.into_iter().collect();
82        Dispatch::new(move |mut program| {
83            program.dispatch_multiple(msg_list);
84        })
85    }
86}
87
88impl<APP> From<Effects<APP::MSG, ()>> for Dispatch<APP>
89where
90    APP: Application,
91{
92    /// Convert Effects that has only follow ups
93    fn from(effects: Effects<APP::MSG, ()>) -> Self {
94        // we can safely ignore the effects here
95        // as there is no content on it.
96        let Effects { local, external: _ } = effects;
97
98        Dispatch::batch(local.into_iter().map(Dispatch::from))
99    }
100}
101
102impl<APP, IN> From<IN> for Dispatch<APP>
103where
104    APP: Application,
105    IN: IntoIterator<Item = Effects<APP::MSG, ()>>,
106{
107    fn from(effects: IN) -> Self {
108        Dispatch::batch(effects.into_iter().map(Dispatch::from))
109    }
110}
111
112impl<APP> From<Cmd<APP::MSG>> for Dispatch<APP>
113where
114    APP: Application,
115{
116    fn from(task: Cmd<APP::MSG>) -> Self {
117        Dispatch::new(move |program| {
118            for mut command in task.commands.into_iter() {
119                let program = program.downgrade();
120                spawn_local(async move {
121                    let mut program = program.upgrade().expect("upgrade");
122                    while let Some(msg) = command.next().await {
123                        program.dispatch(msg)
124                    }
125                });
126            }
127        })
128    }
129}