#![cfg_attr(not(feature = "stable"), feature(unboxed_closures, fn_traits))]
#![deny(missing_docs)]
use futures::FutureExt;
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: SignalSetter<Msg>,
msgs: SmallVec<[Msg; 4]>,
cmds: SmallVec<[CmdFut<Msg>; 4]>,
}
impl<Msg: 'static> Cmd<Msg> {
pub fn new(msg_dispatcher: SignalSetter<Msg>) -> Self {
Self {
msg_dispatcher,
cmds: Default::default(),
msgs: Default::default(),
}
}
pub fn msg(&mut self, msg: Msg) -> &mut Self {
self.msgs.push(msg);
self
}
pub fn batch_msgs<I: IntoIterator<Item = Msg>>(
&mut self,
msgs: I,
) -> &mut Self {
self.msgs.extend(msgs);
self
}
pub fn cmd<Fut, I>(&mut self, cmd: Fut) -> &mut Self
where
Fut: Future<Output = I> + 'static,
I: IntoIterator<Item = Msg>,
{
self
.cmds
.push(Box::pin(cmd.map(|i| i.into_iter().collect())));
self
}
}
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;
for cmds in std::mem::take(&mut self.cmds) {
spawn_local(async move {
let mut cmds = cmds.await.into_iter();
if let Some(msg) = cmds.next() {
msg_dispatcher(msg);
}
for msg in cmds {
spawn_local(async move { msg_dispatcher(msg) });
}
});
}
for msg in std::mem::take(&mut self.msgs) {
queue_microtask(move || msg_dispatcher.set(msg));
}
}
}
pub struct MsgDispatcher<Msg: 'static>(WriteSignal<Msg>);
impl<Msg: 'static> From<WriteSignal<Msg>> for MsgDispatcher<Msg> {
fn from(writer: WriteSignal<Msg>) -> Self {
Self(writer)
}
}
impl<Msg: 'static> Clone for MsgDispatcher<Msg> {
fn clone(&self) -> Self {
Self(self.0)
}
}
impl<Msg: 'static> Copy for MsgDispatcher<Msg> {}
impl<Msg: 'static> SignalSet<Msg> for MsgDispatcher<Msg> {
fn set(&self, new_value: Msg) {
self.0.set(new_value);
}
fn try_set(&self, new_value: Msg) -> Option<Msg> {
self.0.try_set(new_value)
}
}
#[cfg(not(feature = "stable"))]
impl<Msg> FnOnce<(Msg,)> for MsgDispatcher<Msg> {
type Output = ();
extern "rust-call" fn call_once(self, args: (Msg,)) -> Self::Output {
self.set(args.0);
}
}
#[cfg(not(feature = "stable"))]
impl<Msg> FnMut<(Msg,)> for MsgDispatcher<Msg> {
extern "rust-call" fn call_mut(&mut self, args: (Msg,)) -> Self::Output {
self.set(args.0);
}
}
#[cfg(not(feature = "stable"))]
impl<Msg> Fn<(Msg,)> for MsgDispatcher<Msg> {
extern "rust-call" fn call(&self, args: (Msg,)) -> Self::Output {
self.set(args.0);
}
}
impl<Msg> MsgDispatcher<Msg> {
#[inline]
pub fn dispatch(self, msg: Msg) {
self.set(msg);
}
pub fn queue_msg(self, msg: Msg) {
queue_microtask(move || self.dispatch(msg));
}
pub fn batch<I>(self, msgs: I)
where
I: IntoIterator<Item = Msg>,
{
for msg in msgs {
self.dispatch(msg);
}
}
pub fn queue_batch<I>(self, msgs: I)
where
I: IntoIterator<Item = Msg>,
{
for msg in msgs {
queue_microtask(move || self.dispatch(msg));
}
}
}