use crate::{
diagnostics::is_suppressing_resource_load,
owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},
signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},
traits::{DefinedAt, Dispose, GetUntracked, Set, Update},
unwrap_signal,
};
use std::{fmt::Debug, future::Future, panic::Location, pin::Pin, sync::Arc};
pub struct MultiAction<I, O, S = SyncStorage> {
inner: ArenaItem<ArcMultiAction<I, O>, S>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: &'static Location<'static>,
}
impl<I, O, S> Dispose for MultiAction<I, O, S> {
fn dispose(self) {
self.inner.dispose()
}
}
impl<I, O, S> DefinedAt for MultiAction<I, O, S>
where
I: 'static,
O: 'static,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
{
None
}
}
}
impl<I, O, S> Copy for MultiAction<I, O, S>
where
I: 'static,
O: 'static,
{
}
impl<I, O, S> Clone for MultiAction<I, O, S>
where
I: 'static,
O: 'static,
{
fn clone(&self) -> Self {
*self
}
}
impl<I, O> MultiAction<I, O>
where
I: Send + Sync + 'static,
O: Send + Sync + 'static,
{
#[track_caller]
pub fn new<Fut>(
action_fn: impl Fn(&I) -> Fut + Send + Sync + 'static,
) -> Self
where
Fut: Future<Output = O> + Send + 'static,
{
Self {
inner: ArenaItem::new_with_storage(ArcMultiAction::new(action_fn)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
}
}
}
impl<I, O, S> MultiAction<I, O, S>
where
I: Send + Sync + 'static,
O: Send + Sync + 'static,
S: Storage<ArcMultiAction<I, O>>,
{
pub fn dispatch(&self, input: I) {
if !is_suppressing_resource_load() {
self.inner.try_with_value(|inner| inner.dispatch(input));
}
}
pub fn dispatch_sync(&self, value: O) {
self.inner
.try_with_value(|inner| inner.dispatch_sync(value));
}
}
impl<I, O> MultiAction<I, O>
where
I: Send + Sync + 'static,
O: Send + Sync + 'static,
{
pub fn submissions(&self) -> ReadSignal<Vec<ArcSubmission<I, O>>> {
self.inner
.try_with_value(|inner| inner.submissions())
.unwrap_or_else(unwrap_signal!(self))
.into()
}
}
impl<I, O, S> MultiAction<I, O, S>
where
I: 'static,
O: 'static,
S: Storage<ArcMultiAction<I, O>>
+ Storage<ArcReadSignal<Vec<ArcSubmission<I, O>>>>,
{
pub fn version(&self) -> RwSignal<usize> {
self.inner
.try_with_value(|inner| inner.version())
.unwrap_or_else(unwrap_signal!(self))
.into()
}
}
pub struct ArcMultiAction<I, O> {
version: ArcRwSignal<usize>,
submissions: ArcRwSignal<Vec<ArcSubmission<I, O>>>,
#[allow(clippy::complexity)]
action_fn: Arc<
dyn Fn(&I) -> Pin<Box<dyn Future<Output = O> + Send>> + Send + Sync,
>,
}
impl<I, O> Debug for ArcMultiAction<I, O>
where
I: 'static,
O: 'static,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ArcMultiAction")
.field("version", &self.version)
.field("submissions", &self.submissions)
.finish()
}
}
impl<I, O> Clone for ArcMultiAction<I, O>
where
I: 'static,
O: 'static,
{
fn clone(&self) -> Self {
Self {
version: self.version.clone(),
submissions: self.submissions.clone(),
action_fn: Arc::clone(&self.action_fn),
}
}
}
impl<I, O> ArcMultiAction<I, O> {
#[track_caller]
pub fn new<Fut>(
action_fn: impl Fn(&I) -> Fut + Send + Sync + 'static,
) -> Self
where
Fut: Future<Output = O> + Send + 'static,
{
let action_fn = Arc::new(move |input: &I| {
let fut = action_fn(input);
Box::pin(fut) as Pin<Box<dyn Future<Output = O> + Send>>
});
Self {
version: ArcRwSignal::new(0),
submissions: ArcRwSignal::new(Vec::new()),
action_fn,
}
}
}
impl<I, O> ArcMultiAction<I, O>
where
I: Send + Sync + 'static,
O: Send + Sync + 'static,
{
pub fn dispatch(&self, input: I) {
if !is_suppressing_resource_load() {
let fut = (self.action_fn)(&input);
let submission = ArcSubmission {
input: ArcRwSignal::new(Some(input)),
value: ArcRwSignal::new(None),
pending: ArcRwSignal::new(true),
canceled: ArcRwSignal::new(false),
};
self.submissions
.try_update(|subs| subs.push(submission.clone()));
let version = self.version.clone();
crate::spawn(async move {
let new_value = fut.await;
let canceled = submission.canceled.get_untracked();
if !canceled {
submission.value.try_set(Some(new_value));
}
submission.input.try_set(None);
submission.pending.try_set(false);
version.try_update(|n| *n += 1);
})
}
}
pub fn dispatch_sync(&self, value: O) {
let submission = ArcSubmission {
input: ArcRwSignal::new(None),
value: ArcRwSignal::new(Some(value)),
pending: ArcRwSignal::new(false),
canceled: ArcRwSignal::new(false),
};
self.submissions
.try_update(|subs| subs.push(submission.clone()));
self.version.try_update(|n| *n += 1);
}
}
impl<I, O> ArcMultiAction<I, O> {
pub fn submissions(&self) -> ArcReadSignal<Vec<ArcSubmission<I, O>>> {
self.submissions.read_only()
}
pub fn version(&self) -> ArcRwSignal<usize> {
self.version.clone()
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ArcSubmission<I, O> {
input: ArcRwSignal<Option<I>>,
value: ArcRwSignal<Option<O>>,
pending: ArcRwSignal<bool>,
canceled: ArcRwSignal<bool>,
}
impl<I, O> ArcSubmission<I, O>
where
I: 'static,
O: 'static,
{
#[track_caller]
pub fn input(&self) -> ArcReadSignal<Option<I>> {
self.input.read_only()
}
#[track_caller]
pub fn value(&self) -> ArcReadSignal<Option<O>> {
self.value.read_only()
}
#[track_caller]
pub fn pending(&self) -> ArcReadSignal<bool> {
self.pending.read_only()
}
#[track_caller]
pub fn canceled(&self) -> ArcReadSignal<bool> {
self.canceled.read_only()
}
#[track_caller]
pub fn cancel(&self) {
self.canceled.try_set(true);
}
}
impl<I, O> Clone for ArcSubmission<I, O> {
fn clone(&self) -> Self {
Self {
input: self.input.clone(),
value: self.value.clone(),
pending: self.pending.clone(),
canceled: self.canceled.clone(),
}
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Submission<I, O, S = SyncStorage>
where
I: 'static,
O: 'static,
{
input: RwSignal<Option<I>, S>,
value: RwSignal<Option<O>, S>,
pending: RwSignal<bool>,
canceled: RwSignal<bool>,
}
impl<I, O> From<ArcSubmission<I, O>> for Submission<I, O>
where
I: Send + Sync + 'static,
O: Send + Sync + 'static,
{
fn from(value: ArcSubmission<I, O>) -> Self {
let ArcSubmission {
input,
value,
pending,
canceled,
} = value;
Self {
input: input.into(),
value: value.into(),
pending: pending.into(),
canceled: canceled.into(),
}
}
}
impl<I, O> FromLocal<ArcSubmission<I, O>> for Submission<I, O, LocalStorage>
where
I: 'static,
O: 'static,
{
fn from_local(value: ArcSubmission<I, O>) -> Self {
let ArcSubmission {
input,
value,
pending,
canceled,
} = value;
Self {
input: RwSignal::from_local(input),
value: RwSignal::from_local(value),
pending: pending.into(),
canceled: canceled.into(),
}
}
}
impl<I, O, S> Submission<I, O, S>
where
S: Storage<ArcRwSignal<Option<I>>> + Storage<ArcReadSignal<Option<I>>>,
{
#[track_caller]
pub fn input(&self) -> ReadSignal<Option<I>, S> {
self.input.read_only()
}
}
impl<I, O, S> Submission<I, O, S>
where
S: Storage<ArcRwSignal<Option<O>>> + Storage<ArcReadSignal<Option<O>>>,
{
#[track_caller]
pub fn value(&self) -> ReadSignal<Option<O>, S> {
self.value.read_only()
}
}
impl<I, O, S> Submission<I, O, S> {
#[track_caller]
pub fn pending(&self) -> ReadSignal<bool> {
self.pending.read_only()
}
#[track_caller]
pub fn canceled(&self) -> ReadSignal<bool> {
self.canceled.read_only()
}
#[track_caller]
pub fn cancel(&self) {
self.canceled.try_set(true);
}
}
impl<I, O, S> Clone for Submission<I, O, S> {
fn clone(&self) -> Self {
*self
}
}
impl<I, O, S> Copy for Submission<I, O, S> {}