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))
}
}