#![cfg_attr(feature = "nightly", feature(unboxed_closures, fn_traits))]
#![deny(missing_docs)]
#[doc(hidden)]
pub use futures;
use futures::{
channel::mpsc::UnboundedSender,
FutureExt,
SinkExt,
};
#[doc(hidden)]
pub use leptos_reactive;
use leptos_reactive::*;
pub use leptos_tea_macros::*;
use smallvec::SmallVec;
use std::{
future::Future,
pin::Pin,
};
type CmdFut<Msg> = Pin<Box<dyn Future<Output = SmallVec<[Msg; 4]>>>>;
pub struct Cmd<Msg: 'static> {
msg_dispatcher: StoredValue<UnboundedSender<Msg>>,
msgs: SmallVec<[Msg; 4]>,
cmds: SmallVec<[CmdFut<Msg>; 4]>,
}
impl<Msg: 'static> Cmd<Msg> {
#[doc(hidden)]
pub fn new(msg_dispatcher: StoredValue<UnboundedSender<Msg>>) -> Self {
Self {
msg_dispatcher,
cmds: Default::default(),
msgs: Default::default(),
}
}
pub fn msg(&mut self, msg: Msg) {
self.msgs.push(msg);
}
pub fn batch_msgs<I: IntoIterator<Item = Msg>>(&mut self, msgs: I) {
self.msgs.extend(msgs);
}
pub fn cmd<Fut, I>(&mut self, cmd: Fut)
where
Fut: Future<Output = I> + 'static,
I: IntoIterator<Item = Msg>,
{
self
.cmds
.push(Box::pin(cmd.map(|i| i.into_iter().collect())));
}
pub fn perform(&mut self) {
Self {
msg_dispatcher: self.msg_dispatcher,
msgs: core::mem::take(&mut self.msgs),
cmds: core::mem::take(&mut self.cmds),
};
}
}
impl<Msg: 'static> Clone for Cmd<Msg> {
fn clone(&self) -> Self {
Self {
msg_dispatcher: self.msg_dispatcher,
msgs: Default::default(),
cmds: Default::default(),
}
}
}
impl<Msg: 'static> Drop for Cmd<Msg> {
fn drop(&mut self) {
let msg_dispatcher = self.msg_dispatcher.get_value();
for cmds in std::mem::take(&mut self.cmds) {
let mut msg_dispatcher = msg_dispatcher.clone();
spawn_local(async move {
let mut cmds = cmds.await.into_iter();
if let Some(msg) = cmds.next() {
msg_dispatcher.send(msg).await.unwrap();
}
for msg in cmds {
let mut msg_dispatcher = msg_dispatcher.clone();
spawn_local(async move { msg_dispatcher.send(msg).await.unwrap() });
}
});
}
for msg in std::mem::take(&mut self.msgs) {
let mut msg_dispatcher = msg_dispatcher.clone();
spawn_local(async move {
msg_dispatcher.send(msg).await.unwrap();
});
}
}
}
pub struct MsgDispatcher<Msg: 'static>(StoredValue<UnboundedSender<Msg>>);
impl<Msg: 'static> Clone for MsgDispatcher<Msg> {
fn clone(&self) -> Self {
Self(self.0)
}
}
impl<Msg: 'static> Copy for MsgDispatcher<Msg> {}
#[cfg(feature = "nightly")]
impl<Msg> FnOnce<(Msg,)> for MsgDispatcher<Msg> {
type Output = ();
extern "rust-call" fn call_once(self, args: (Msg,)) -> Self::Output {
self.dispatch(args.0);
}
}
#[cfg(feature = "nightly")]
impl<Msg> FnMut<(Msg,)> for MsgDispatcher<Msg> {
extern "rust-call" fn call_mut(&mut self, args: (Msg,)) -> Self::Output {
self.dispatch(args.0);
}
}
#[cfg(feature = "nightly")]
impl<Msg> Fn<(Msg,)> for MsgDispatcher<Msg> {
extern "rust-call" fn call(&self, args: (Msg,)) -> Self::Output {
self.dispatch(args.0);
}
}
impl<Msg> MsgDispatcher<Msg> {
#[doc(hidden)]
pub fn new(msg_dispatcher: StoredValue<UnboundedSender<Msg>>) -> Self {
Self(msg_dispatcher)
}
pub fn dispatch(self, msg: Msg) {
let mut msg_dispatcher = self.0.get_value();
spawn_local(async move {
msg_dispatcher.send(msg).await.unwrap();
});
}
pub fn dispatch_immediate(self, msg: Msg) {
let msg_dispatcher = self.0.get_value();
msg_dispatcher.unbounded_send(msg).unwrap();
}
pub fn batch<I>(self, msgs: I)
where
I: IntoIterator<Item = Msg>,
{
for msg in msgs {
self.dispatch(msg);
}
}
}