1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! provides functionalities for commands to be executed by the system, such as
//! when the application starts or after the application updates.
//!
use crate::dom::Program;
use crate::dom::{Application, Effects, Modifier};

/// Cmd is a command to be executed by the system.
/// This is returned at the init function of a component and is executed right
/// after instantiation of that component.
/// Cmd required a DSP object which is the Program as an argument
/// The emit function is called with the program argument.
/// The callback is supplied with the program an is then executed/emitted.
pub struct Cmd<APP, MSG>
where
    MSG: 'static,
{
    /// the functions that would be executed when this Cmd is emited
    pub commands: Vec<Box<dyn FnOnce(Program<APP, MSG>)>>,
    pub(crate) modifier: Modifier,
}

impl<APP, MSG> Cmd<APP, MSG>
where
    MSG: 'static,
    APP: Application<MSG> + 'static,
{
    /// creates a new Cmd from a function
    pub fn new<F>(f: F) -> Self
    where
        F: FnOnce(Program<APP, MSG>) + 'static,
    {
        Self {
            commands: vec![Box::new(f)],
            modifier: Default::default(),
        }
    }

    /// When you need the runtime to perform couple of commands, you can batch
    /// then together.
    pub fn batch(cmds: impl IntoIterator<Item = Self>) -> Self {
        let mut commands = vec![];
        let mut should_update_view = false;
        let mut log_measurements = false;
        for cmd in cmds {
            if cmd.modifier.should_update_view {
                should_update_view = true;
            }
            if cmd.modifier.log_measurements {
                log_measurements = true;
            }
            commands.extend(cmd.commands);
        }
        Self {
            commands,
            modifier: Modifier {
                should_update_view,
                log_measurements,
                ..Default::default()
            },
        }
    }

    /// Add a cmd
    pub fn push(&mut self, cmd: Self) {
        self.append([cmd])
    }

    /// Append more cmd into this cmd and return self
    pub fn append(&mut self, cmds: impl IntoIterator<Item = Self>) {
        for cmd in cmds {
            if cmd.modifier.should_update_view {
                self.modifier.should_update_view = true;
            }
            if cmd.modifier.log_measurements {
                self.modifier.log_measurements = true;
            }
            self.commands.extend(cmd.commands);
        }
    }

    /// Tell the runtime that there are no commands.
    pub fn none() -> Self {
        Cmd {
            commands: vec![],
            modifier: Default::default(),
        }
    }

    /// Modify the Cmd such that whether or not it will update the view set by `should_update_view`
    /// when the cmd is executed in the program
    pub fn should_update_view(mut self, should_update_view: bool) -> Self {
        self.modifier.should_update_view = should_update_view;
        self
    }

    /// Modify the command such that it will not do an update on the view when it is executed.
    pub fn no_render(mut self) -> Self {
        self.modifier.should_update_view = false;
        self
    }

    /// Modify the command such that it will log measurement when it is executed
    pub fn measure(mut self) -> Self {
        self.modifier.log_measurements = true;
        self
    }

    /// Modify the Cmd such that it will log a measuregment when it is executed
    /// The `measurement_name` is set to distinguish the measurements from each other.
    pub fn measure_with_name(mut self, name: &str) -> Self {
        self = self.measure();
        self.modifier.measurement_name = name.to_string();
        self
    }

    /// Executes the Cmd
    pub fn emit(self, program: &Program<APP, MSG>) {
        for cb in self.commands {
            let program_clone = program.clone();
            cb(program_clone);
        }
    }

    /// Tell the runtime to execute subsequent update of the App with the message list.
    /// A single call to update the view is then executed thereafter.
    ///
    pub fn batch_msg(msg_list: impl IntoIterator<Item = MSG>) -> Self {
        let msg_list: Vec<MSG> = msg_list.into_iter().collect();
        Cmd::new(move |program| {
            program.dispatch_multiple(msg_list);
        })
    }
}

impl<APP, MSG> From<Effects<MSG, ()>> for Cmd<APP, MSG>
where
    MSG: 'static,
    APP: Application<MSG> + 'static,
{
    /// Convert Effects that has only follow ups
    fn from(effects: Effects<MSG, ()>) -> Self {
        // we can safely ignore the effects here
        // as there is no content on it.
        let Effects {
            local,
            external: _,
            modifier,
        } = effects;
        let mut cmd = Cmd::batch_msg(local);
        cmd.modifier = modifier;
        cmd
    }
}

impl<APP, MSG> From<Vec<Effects<MSG, ()>>> for Cmd<APP, MSG>
where
    MSG: 'static,
    APP: Application<MSG> + 'static,
{
    fn from(effects: Vec<Effects<MSG, ()>>) -> Self {
        Cmd::from(Effects::merge_all(effects))
    }
}