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 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
//! provides functionalities for commands to be executed by the system, such as
//! when the application starts or after the application updates.
//!
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.
pub struct Cmd<DSP> {
/// the functions that would be executed when this Cmd is emited
pub commands: Vec<Box<dyn FnOnce(DSP)>>,
pub(crate) modifier: Modifier,
}
/// These are collections of fields where we can modify the Cmd
/// such as logging measurement or should update the view
#[derive(Clone)]
pub struct Modifier {
/// 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,
/// Set the measurement name for this Cmd.
/// This is used to distinguish measurements from other measurements in different parts of you
/// application
///
/// This measurment name will be copied
/// into the [`Measurement`](crate::Measurement) passed in
/// [`Application::measurement`](crate::Application::measurement)
pub measurement_name: String,
}
impl Default for Modifier {
fn default() -> Self {
Self {
// every cmd will update the view by default
should_update_view: true,
// every cmd will not log measurement by default
log_measurements: false,
// empty string by default
measurement_name: String::new(),
}
}
}
impl<DSP> Cmd<DSP>
where
DSP: 'static,
{
/// creates a new Cmd from a function
pub fn new<F>(f: F) -> Self
where
F: FnOnce(DSP) + '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()
},
}
}
/// Append more cmd into this cmd and return self
pub fn append(mut self, cmds: impl IntoIterator<Item = Self>) -> 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);
}
self
}
/// 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
}
}
impl<DSP> Cmd<DSP>
where
DSP: Clone + 'static,
{
/// Executes the Cmd
pub fn emit(self, program: &DSP) {
for cb in self.commands {
let program_clone = program.clone();
cb(program_clone);
}
}
}
impl<DSP> Cmd<DSP> {
/// 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>(msg_list: Vec<MSG>) -> Self
where
MSG: 'static,
DSP: Dispatch<MSG> + Clone + 'static,
{
Cmd::new(move |program: DSP| {
program.dispatch_multiple(msg_list);
})
}
}
impl<DSP, MSG> From<Effects<MSG, ()>> for Cmd<DSP>
where
MSG: 'static,
DSP: Dispatch<MSG> + Clone + '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
}
}