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
164
165
166
//! provides functionalities for commands to be executed by the system, such as
//! when the application starts or after the application updates.
//!
use crate::Callback;
use crate::Dispatch;
use crate::Effects;

/// 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.
#[derive(Clone)]
pub struct Cmd<DSP> {
    /// the functions that would be executed when this Cmd is emited
    pub commands: Vec<Callback<DSP, ()>>,
    /// this instruct the program whether or not to update the view
    pub should_update_view: bool,
    /// tell the cmd to log the measurements of not.
    /// set only to true for a certain MSG where you want to measure the performance
    /// in the component update function. Otherwise, measurement calls
    /// for other non-trivial functions are also called causing on measurements.
    pub log_measurements: bool,
}

impl<DSP> Cmd<DSP>
where
    DSP: Clone + 'static,
{
    /// creates a new Cmd from a function
    pub fn new<F>(f: F) -> Self
    where
        F: Fn(DSP) + 'static,
    {
        Self {
            commands: vec![Callback::from(f)],
            should_update_view: true,
            log_measurements: false,
        }
    }

    /// creates a unified Cmd which batches all the other Cmds in one.
    pub fn batch(cmds: Vec<Self>) -> Self {
        let should_update_view = cmds.iter().any(|c| c.should_update_view);
        let log_measurements = cmds.iter().any(|c| c.log_measurements);
        let mut commands = vec![];
        for cmd in cmds {
            commands.extend(cmd.commands);
        }
        Self {
            commands,
            should_update_view,
            log_measurements,
        }
    }

    /// A Cmd with no callback, similar to NoOp.
    pub fn none() -> Self {
        Cmd {
            commands: vec![],
            should_update_view: true,
            log_measurements: false,
        }
    }

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

    /// Creates an empty Cmd and specifies that there is NO update will be made to the
    /// view.
    pub fn should_update_view(should_update_view: bool) -> Self {
        Self {
            commands: vec![],
            should_update_view,
            log_measurements: false,
        }
    }

    /// Create a cmd which instruct the program that there is NO update
    /// will be made to the view
    pub fn no_render() -> Self {
        Self::should_update_view(false)
    }

    /// Create a cmd which instruct the program that it should log the measurements
    /// for each part of the dispatch process.
    pub fn measure() -> Self {
        Self {
            commands: vec![],
            should_update_view: true,
            log_measurements: true,
        }
    }
}

impl<DSP> Cmd<DSP> {
    /// batch dispatch this msg on the next update loop
    pub fn batch_msg<MSG>(msg_list: Vec<MSG>) -> Self
    where
        MSG: Clone + 'static,
        DSP: Dispatch<MSG> + Clone + 'static,
    {
        Cmd::new(move |program: DSP| {
            for msg in msg_list.iter() {
                program.dispatch(msg.clone());
            }
        })
    }

    /// Convert effects into Cmd
    pub fn map_msg<F, PMSG, MSG>(effects: Effects<MSG, PMSG>, f: F) -> Self
    where
        DSP: Dispatch<PMSG> + Clone + 'static,
        MSG: Clone + 'static,
        PMSG: Clone + 'static,
        F: Fn(MSG) -> PMSG + 'static,
    {
        let Effects {
            follow_ups,
            effects,
        } = effects;

        Cmd::batch_msg(
            effects
                .into_iter()
                .chain(follow_ups.into_iter().map(f))
                .collect(),
        )
    }

    /// Create a Cmd from effects that has follow_ups and effects are both Vec<MSG>
    pub fn from_effects<MSG>(effects: Effects<MSG, MSG>) -> Self
    where
        MSG: Clone + 'static,
        DSP: Dispatch<MSG> + Clone + 'static,
    {
        let Effects {
            effects,
            mut follow_ups,
        } = effects;
        let mut all = effects;
        all.append(&mut follow_ups);
        Self::batch_msg(all)
    }
}

impl<DSP, MSG> From<Effects<MSG, ()>> for Cmd<DSP>
where
    MSG: Clone + 'static,
    DSP: Dispatch<MSG> + Clone + 'static,
{
    /// Convert Effects that has only follow ups
    fn from(effects: Effects<MSG, ()>) -> Self {
        let Effects {
            follow_ups,
            effects: _,
        } = effects;
        Cmd::batch_msg(follow_ups)
    }
}