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
//! 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, Cmd, Effects};
use wasm_bindgen_futures::spawn_local;
/// Dispatch 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.
/// Dispatch 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 Dispatch<APP>
where
APP: Application,
{
/// the functions that would be executed when this Dispatch is emited
#[allow(clippy::type_complexity)]
pub(crate) commands: Vec<Box<dyn FnOnce(Program<APP>)>>,
}
impl<APP> Dispatch<APP>
where
APP: Application,
{
/// creates a new Dispatch from a function
pub fn new<F>(f: F) -> Self
where
F: FnOnce(Program<APP>) + 'static,
{
Self {
commands: vec![Box::new(f)],
}
}
/// 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![];
for cmd in cmds {
commands.extend(cmd.commands);
}
Self { commands }
}
/// 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 {
self.commands.extend(cmd.commands);
}
}
/// Tell the runtime that there are no commands.
pub fn none() -> Self {
Dispatch { commands: vec![] }
}
/// returns true if commands is empty
pub fn is_empty(&self) -> bool {
self.commands.is_empty()
}
/// Executes the Dispatch
pub(crate) fn emit(self, program: Program<APP>) {
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 = APP::MSG>) -> Self {
let msg_list: Vec<APP::MSG> = msg_list.into_iter().collect();
Dispatch::new(move |mut program| {
program.dispatch_multiple(msg_list);
})
}
}
impl<APP> From<Effects<APP::MSG, ()>> for Dispatch<APP>
where
APP: Application,
{
/// Convert Effects that has only follow ups
fn from(effects: Effects<APP::MSG, ()>) -> Self {
// we can safely ignore the effects here
// as there is no content on it.
let Effects { local, external: _ } = effects;
Dispatch::batch(local.into_iter().map(Dispatch::from))
}
}
impl<APP, IN> From<IN> for Dispatch<APP>
where
APP: Application,
IN: IntoIterator<Item = Effects<APP::MSG, ()>>,
{
fn from(effects: IN) -> Self {
Dispatch::batch(effects.into_iter().map(Dispatch::from))
}
}
impl<APP> From<Cmd<APP::MSG>> for Dispatch<APP>
where
APP: Application,
{
fn from(task: Cmd<APP::MSG>) -> Self {
Dispatch::new(move |program| {
for mut command in task.commands.into_iter() {
let program = program.downgrade();
spawn_local(async move {
let mut program = program.upgrade().expect("upgrade");
while let Some(msg) = command.next().await {
program.dispatch(msg)
}
});
}
})
}
}