#![cfg_attr(not(feature = "stable"), feature(unboxed_closures, fn_traits))]
#![deny(missing_docs)]
use futures::FutureExt;
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: RwSignal<Option<Msg>>,
msgs: SmallVec<[Msg; 4]>,
cmds: SmallVec<[CmdFut<Msg>; 4]>,
}
impl<Msg: 'static> Cmd<Msg> {
pub fn new(msg_dispatcher: RwSignal<Option<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;
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.set(Some(msg));
}
for msg in cmds {
spawn_local(async move { msg_dispatcher.set(Some(msg)) });
}
});
}
for msg in std::mem::take(&mut self.msgs) {
wasm_bindgen_futures::spawn_local(async move {
msg_dispatcher.set(Some(msg))
});
}
}
}
pub struct MsgDispatcher<Msg: 'static>(RwSignal<Option<Msg>>);
impl<Msg: 'static> From<RwSignal<Option<Msg>>> for MsgDispatcher<Msg> {
fn from(writer: RwSignal<Option<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) {
let dispatcher = self.0;
wasm_bindgen_futures::spawn_local(async move {
dispatcher.set(Some(new_value))
});
}
fn try_set(&self, new_value: Msg) -> Option<Msg> {
self.0.try_set(Some(new_value)).flatten()
}
}
#[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.dispatch(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.dispatch(args.0);
}
}
#[cfg(not(feature = "stable"))]
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> {
#[inline]
pub fn dispatch(self, msg: Msg) {
self.set(msg);
}
pub fn dispatch_immediate(self, msg: Msg) {
self.0.set(Some(msg));
}
pub fn batch<I>(self, msgs: I)
where
I: IntoIterator<Item = Msg>,
{
for msg in msgs {
self.dispatch(msg);
}
}
}