mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
feat: allow !Send
signals
This commit is contained in:
parent
d4ec5e187b
commit
1f2b13a976
37 changed files with 1323 additions and 710 deletions
|
@ -76,18 +76,9 @@ pub fn Counters() -> impl IntoView {
|
|||
</nav>
|
||||
<main>
|
||||
<FlatRoutes fallback=|| "Not found.">
|
||||
<Route
|
||||
path=StaticSegment("")
|
||||
view=Counter
|
||||
/>
|
||||
<Route
|
||||
path=StaticSegment("form")
|
||||
view=FormCounter
|
||||
/>
|
||||
<Route
|
||||
path=StaticSegment("multi")
|
||||
view=MultiuserCounter
|
||||
/>
|
||||
<Route path=StaticSegment("") view=Counter/>
|
||||
<Route path=StaticSegment("form") view=FormCounter/>
|
||||
<Route path=StaticSegment("multi") view=MultiuserCounter/>
|
||||
</FlatRoutes>
|
||||
</main>
|
||||
</Router>
|
||||
|
@ -124,10 +115,7 @@ pub fn Counter() -> impl IntoView {
|
|||
<div>
|
||||
<button on:click=move |_| clear.dispatch(())>"Clear"</button>
|
||||
<button on:click=move |_| dec.dispatch(())>"-1"</button>
|
||||
<span>
|
||||
"Value: "
|
||||
<Suspense>{counter} "!" </Suspense>
|
||||
</span>
|
||||
<span>"Value: " <Suspense>{counter} "!"</Suspense></span>
|
||||
<button on:click=move |_| inc.dispatch(())>"+1"</button>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
|
|
|
@ -31,7 +31,6 @@ pin-project-lite = "0.2.13"
|
|||
dashmap = { version = "5.5.3", optional = true }
|
||||
once_cell = { version = "1.19.0", optional = true }
|
||||
async-broadcast = { version = "0.6.0", optional = true }
|
||||
send_wrapper = "0.6.0"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use futures::StreamExt;
|
||||
use http::Method;
|
||||
use leptos::{html::Input, prelude::*, spawn::spawn_local};
|
||||
use send_wrapper::SendWrapper;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use server_fn::{
|
||||
client::{browser::BrowserClient, Client},
|
||||
|
@ -362,10 +361,9 @@ pub fn FileUpload() -> impl IntoView {
|
|||
Ok(count)
|
||||
}
|
||||
|
||||
let upload_action = Action::new(|data: &SendWrapper<FormData>| {
|
||||
let data = (**data).clone();
|
||||
let upload_action = Action::new_local(|data: &FormData| {
|
||||
// `MultipartData` implements `From<FormData>`
|
||||
file_length(data.into())
|
||||
file_length(data.clone().into())
|
||||
});
|
||||
|
||||
view! {
|
||||
|
@ -375,14 +373,15 @@ pub fn FileUpload() -> impl IntoView {
|
|||
ev.prevent_default();
|
||||
let target = ev.target().unwrap().unchecked_into::<HtmlFormElement>();
|
||||
let form_data = FormData::new_with_form(&target).unwrap();
|
||||
upload_action.dispatch(SendWrapper::new(form_data));
|
||||
upload_action.dispatch_local(form_data);
|
||||
}>
|
||||
<input type="file" name="file_to_upload"/>
|
||||
<input type="submit"/>
|
||||
</form>
|
||||
<p>
|
||||
{move || {
|
||||
if upload_action.input().get().is_none() && upload_action.value().get().is_none() {
|
||||
if upload_action.input().read().is_none() && upload_action.value().read().is_none()
|
||||
{
|
||||
"Upload a file.".to_string()
|
||||
} else if upload_action.pending().get() {
|
||||
"Uploading...".to_string()
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
//!
|
||||
//! Use `SyncCallback` when you want the function to be `Sync` and `Send`.
|
||||
|
||||
use reactive_graph::owner::StoredValue;
|
||||
use reactive_graph::owner::{StoredValue, SyncStorage};
|
||||
use std::{fmt, rc::Rc, sync::Arc};
|
||||
|
||||
/// A wrapper trait for calling callbacks.
|
||||
|
@ -49,30 +49,7 @@ pub trait Callable<In: 'static, Out: 'static = ()> {
|
|||
fn call(&self, input: In) -> Out;
|
||||
}
|
||||
|
||||
/// Callbacks define a standard way to store functions and closures.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use leptos::*;
|
||||
/// # use leptos::{Callable, Callback};
|
||||
/// #[component]
|
||||
/// fn MyComponent(
|
||||
/// #[prop(into)] render_number: Callback<i32, String>,
|
||||
/// ) -> impl IntoView {
|
||||
/// view! {
|
||||
/// <div>
|
||||
/// {render_number.call(42)}
|
||||
/// </div>
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn test() -> impl IntoView {
|
||||
/// view! {
|
||||
/// <MyComponent render_number=move |x: i32| x.to_string()/>
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
/// A callback type that is not required to be `Send + Sync`.
|
||||
pub struct UnsyncCallback<In: 'static, Out: 'static = ()>(
|
||||
Rc<dyn Fn(In) -> Out>,
|
||||
);
|
||||
|
@ -168,10 +145,31 @@ impl<In, Out> Fn<(In,)> for UnsyncCallback<In, Out> {
|
|||
}
|
||||
|
||||
// TODO update these docs to swap the two
|
||||
/// A callback type that is `Send` and `Sync` if its input type is `Send` and `Sync`.
|
||||
/// Otherwise, you can use exactly the way you use [`Callback`].
|
||||
/// Callbacks define a standard way to store functions and closures.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use leptos::*;
|
||||
/// # use leptos::{Callable, Callback};
|
||||
/// #[component]
|
||||
/// fn MyComponent(
|
||||
/// #[prop(into)] render_number: Callback<i32, String>,
|
||||
/// ) -> impl IntoView {
|
||||
/// view! {
|
||||
/// <div>
|
||||
/// {render_number.call(42)}
|
||||
/// </div>
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn test() -> impl IntoView {
|
||||
/// view! {
|
||||
/// <MyComponent render_number=move |x: i32| x.to_string()/>
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Callback<In, Out = ()>(
|
||||
StoredValue<Arc<dyn Fn(In) -> Out + Send + Sync>>,
|
||||
StoredValue<Arc<dyn Fn(In) -> Out + Send + Sync>, SyncStorage>,
|
||||
)
|
||||
where
|
||||
In: 'static,
|
||||
|
@ -289,9 +287,7 @@ mod tests {
|
|||
|
||||
let _callback: UnsyncCallback<String, HtmlElement<AnyElement>> =
|
||||
(|x: String| {
|
||||
view! {
|
||||
<h1>{x}</h1>
|
||||
}
|
||||
view! { <h1>{x}</h1> }
|
||||
})
|
||||
.into();
|
||||
rt.dispose();
|
||||
|
@ -315,9 +311,7 @@ mod tests {
|
|||
|
||||
let _callback: Callback<String, HtmlElement<AnyElement>> =
|
||||
(|x: String| {
|
||||
view! {
|
||||
<h1>{x}</h1>
|
||||
}
|
||||
view! { <h1>{x}</h1> }
|
||||
})
|
||||
.into();
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ use leptos_macro::component;
|
|||
use reactive_graph::{
|
||||
computed::{suspense::SuspenseContext, ArcMemo},
|
||||
effect::Effect,
|
||||
owner::{provide_context, Owner},
|
||||
owner::{provide_context, Owner, SyncStorage},
|
||||
signal::ArcRwSignal,
|
||||
traits::{Get, Track, With},
|
||||
traits::{Get, Set, Track, With},
|
||||
wrappers::write::SignalSetter,
|
||||
};
|
||||
use slotmap::{DefaultKey, SlotMap};
|
||||
|
@ -25,7 +25,7 @@ pub fn Transition<Chil>(
|
|||
/// the `pending` state, with its argument indicating whether it is pending (`true`)
|
||||
/// or not pending (`false`).
|
||||
#[prop(optional, into)]
|
||||
set_pending: Option<SignalSetter<bool>>,
|
||||
set_pending: Option<SignalSetter<bool, SyncStorage>>,
|
||||
children: TypedChildren<Chil>,
|
||||
) -> impl IntoView
|
||||
where
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use reactive_graph::{
|
||||
actions::{Action, ArcAction},
|
||||
owner::use_context,
|
||||
owner::{use_context, SyncStorage},
|
||||
traits::DefinedAt,
|
||||
};
|
||||
use server_fn::{error::ServerFnErrorSerde, ServerFn, ServerFnError};
|
||||
|
@ -121,7 +121,7 @@ where
|
|||
S: ServerFn + 'static,
|
||||
S::Output: 'static,
|
||||
{
|
||||
inner: Action<S, Result<S::Output, ServerFnError<S::Error>>>,
|
||||
inner: Action<S, Result<S::Output, ServerFnError<S::Error>>, SyncStorage>,
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
}
|
||||
|
@ -171,7 +171,8 @@ where
|
|||
S::Output: Send + Sync + 'static,
|
||||
S::Error: Send + Sync + 'static,
|
||||
{
|
||||
type Target = Action<S, Result<S::Output, ServerFnError<S::Error>>>;
|
||||
type Target =
|
||||
Action<S, Result<S::Output, ServerFnError<S::Error>>, SyncStorage>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
|
@ -179,7 +180,7 @@ where
|
|||
}
|
||||
|
||||
impl<S> From<ServerAction<S>>
|
||||
for Action<S, Result<S::Output, ServerFnError<S::Error>>>
|
||||
for Action<S, Result<S::Output, ServerFnError<S::Error>>, SyncStorage>
|
||||
where
|
||||
S: ServerFn + 'static,
|
||||
S::Output: 'static,
|
||||
|
|
|
@ -7,7 +7,7 @@ use reactive_graph::{
|
|||
AnySource, AnySubscriber, ReactiveNode, Source, Subscriber,
|
||||
ToAnySource, ToAnySubscriber,
|
||||
},
|
||||
owner::use_context,
|
||||
owner::{use_context, LocalStorage},
|
||||
signal::guards::{AsyncPlain, ReadGuard},
|
||||
traits::{DefinedAt, ReadUntracked},
|
||||
};
|
||||
|
@ -169,7 +169,7 @@ impl<T> Subscriber for ArcLocalResource<T> {
|
|||
}
|
||||
|
||||
pub struct LocalResource<T> {
|
||||
data: AsyncDerived<T>,
|
||||
data: AsyncDerived<T, LocalStorage>,
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
}
|
||||
|
@ -184,9 +184,9 @@ impl<T> Copy for LocalResource<T> {}
|
|||
|
||||
impl<T> LocalResource<T> {
|
||||
#[track_caller]
|
||||
pub fn new<Fut>(fetcher: impl Fn() -> Fut + Send + Sync + 'static) -> Self
|
||||
pub fn new<Fut>(fetcher: impl Fn() -> Fut + 'static) -> Self
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
T: 'static,
|
||||
Fut: Future<Output = T> + Send + 'static,
|
||||
{
|
||||
let fetcher = move || {
|
||||
|
@ -203,7 +203,7 @@ impl<T> LocalResource<T> {
|
|||
}
|
||||
};
|
||||
Self {
|
||||
data: AsyncDerived::new(fetcher),
|
||||
data: AsyncDerived::new_unsync(fetcher),
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use reactive_graph::{
|
||||
actions::{ArcMultiAction, MultiAction},
|
||||
owner::SyncStorage,
|
||||
traits::DefinedAt,
|
||||
};
|
||||
use server_fn::{ServerFn, ServerFnError};
|
||||
|
@ -92,13 +93,14 @@ where
|
|||
S: ServerFn + 'static,
|
||||
S::Output: 'static,
|
||||
{
|
||||
inner: MultiAction<S, Result<S::Output, ServerFnError<S::Error>>>,
|
||||
inner:
|
||||
MultiAction<S, Result<S::Output, ServerFnError<S::Error>>, SyncStorage>,
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
}
|
||||
|
||||
impl<S> From<ServerMultiAction<S>>
|
||||
for MultiAction<S, Result<S::Output, ServerFnError<S::Error>>>
|
||||
for MultiAction<S, Result<S::Output, ServerFnError<S::Error>>, SyncStorage>
|
||||
where
|
||||
S: ServerFn + 'static,
|
||||
S::Output: 'static,
|
||||
|
@ -148,7 +150,8 @@ where
|
|||
S::Output: 'static,
|
||||
S::Error: 'static,
|
||||
{
|
||||
type Target = MultiAction<S, Result<S::Output, ServerFnError<S::Error>>>;
|
||||
type Target =
|
||||
MultiAction<S, Result<S::Output, ServerFnError<S::Error>>, SyncStorage>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
|
|
|
@ -20,7 +20,7 @@ use reactive_graph::{
|
|||
AsyncDerivedRefFuture,
|
||||
},
|
||||
graph::{Source, ToAnySubscriber},
|
||||
owner::Owner,
|
||||
owner::{Owner, SyncStorage},
|
||||
prelude::*,
|
||||
};
|
||||
use std::{future::IntoFuture, ops::Deref};
|
||||
|
@ -400,7 +400,7 @@ where
|
|||
T: Send + Sync + 'static,
|
||||
{
|
||||
ser: PhantomData<Ser>,
|
||||
data: AsyncDerived<T>,
|
||||
data: AsyncDerived<T, SyncStorage>,
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static, Ser> Copy for Resource<T, Ser> {}
|
||||
|
@ -415,7 +415,7 @@ impl<T, Ser> Deref for Resource<T, Ser>
|
|||
where
|
||||
T: Send + Sync + 'static,
|
||||
{
|
||||
type Target = AsyncDerived<T>;
|
||||
type Target = AsyncDerived<T, SyncStorage>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
computed::{ArcMemo, Memo},
|
||||
diagnostics::is_suppressing_resource_load,
|
||||
owner::{Owner, StoredValue},
|
||||
owner::{LocalStorage, Owner, Storage, StoredValue, SyncStorage},
|
||||
signal::{ArcRwSignal, RwSignal},
|
||||
traits::{DefinedAt, Dispose, Get, GetUntracked, Update},
|
||||
unwrap_signal,
|
||||
|
@ -88,11 +88,7 @@ use std::{future::Future, panic::Location, pin::Pin, sync::Arc};
|
|||
/// // if there are multiple arguments, use a tuple
|
||||
/// let action3 = ArcAction::new(|input: &(usize, String)| async { todo!() });
|
||||
/// ```
|
||||
pub struct ArcAction<I, O>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
pub struct ArcAction<I, O> {
|
||||
in_flight: ArcRwSignal<usize>,
|
||||
input: ArcRwSignal<Option<I>>,
|
||||
value: ArcRwSignal<Option<O>>,
|
||||
|
@ -105,11 +101,7 @@ where
|
|||
defined_at: &'static Location<'static>,
|
||||
}
|
||||
|
||||
impl<I, O> Clone for ArcAction<I, O>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
impl<I, O> Clone for ArcAction<I, O> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
in_flight: self.in_flight.clone(),
|
||||
|
@ -261,7 +253,13 @@ where
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> ArcAction<I, O>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
/// Calls the `async` function with a reference to the input type as its argument,
|
||||
/// ensuring that it is spawned on the current thread.
|
||||
#[track_caller]
|
||||
|
@ -318,7 +316,7 @@ where
|
|||
impl<I, O> ArcAction<I, O>
|
||||
where
|
||||
I: 'static,
|
||||
O: Send + Sync + 'static,
|
||||
O: 'static,
|
||||
{
|
||||
/// Creates a new action, which will only be run on the thread in which it is created.
|
||||
///
|
||||
|
@ -571,23 +569,19 @@ where
|
|||
/// // if there are multiple arguments, use a tuple
|
||||
/// let action3 = Action::new(|input: &(usize, String)| async { todo!() });
|
||||
/// ```
|
||||
pub struct Action<I, O>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
inner: StoredValue<ArcAction<I, O>>,
|
||||
pub struct Action<I, O, S = SyncStorage> {
|
||||
inner: StoredValue<ArcAction<I, O>, S>,
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
}
|
||||
|
||||
impl<I: 'static, O: 'static> Dispose for Action<I, O> {
|
||||
impl<I, O, S> Dispose for Action<I, O, S> {
|
||||
fn dispose(self) {
|
||||
self.inner.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> Action<I, O>
|
||||
impl<I, O> Action<I, O, SyncStorage>
|
||||
where
|
||||
I: Send + Sync + 'static,
|
||||
O: Send + Sync + 'static,
|
||||
|
@ -672,7 +666,52 @@ where
|
|||
defined_at: Location::caller(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> Action<I, O, LocalStorage>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
/// Creates a new action, which does not require its inputs or outputs to be `Send`. In all other
|
||||
/// ways, this is the same as [`Action::new`]. If this action is accessed from outside the
|
||||
/// thread on which it was created, it panics.
|
||||
#[track_caller]
|
||||
pub fn new_local<F, Fu>(action_fn: F) -> Self
|
||||
where
|
||||
F: Fn(&I) -> Fu + 'static,
|
||||
Fu: Future<Output = O> + Send + 'static,
|
||||
{
|
||||
Self {
|
||||
inner: StoredValue::new_local(ArcAction::new_unsync(action_fn)),
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new action with the initial value, which does not require its inputs or outputs to be `Send`. In all other
|
||||
/// ways, this is the same as [`Action::new_with_value`]. If this action is accessed from outside the
|
||||
/// thread on which it was created, it panics.
|
||||
#[track_caller]
|
||||
pub fn new_local_with_value<F, Fu>(value: Option<O>, action_fn: F) -> Self
|
||||
where
|
||||
F: Fn(&I) -> Fu + 'static,
|
||||
Fu: Future<Output = O> + Send + 'static,
|
||||
{
|
||||
Self {
|
||||
inner: StoredValue::new_local(ArcAction::new_unsync_with_value(
|
||||
value, action_fn,
|
||||
)),
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O, S> Action<I, O, S>
|
||||
where
|
||||
S: Storage<ArcAction<I, O>>,
|
||||
{
|
||||
/// The number of times the action has successfully completed.
|
||||
///
|
||||
/// ```rust
|
||||
|
@ -696,7 +735,7 @@ where
|
|||
/// # });
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn version(&self) -> RwSignal<usize> {
|
||||
pub fn version(&self) -> RwSignal<usize, SyncStorage> {
|
||||
let inner = self
|
||||
.inner
|
||||
.try_with_value(|inner| inner.version())
|
||||
|
@ -704,6 +743,44 @@ where
|
|||
inner.into()
|
||||
}
|
||||
|
||||
/// Whether the action has been dispatched and is currently waiting to resolve.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use reactive_graph::actions::*;
|
||||
/// # use reactive_graph::prelude::*;
|
||||
/// # tokio_test::block_on(async move {
|
||||
/// # any_spawner::Executor::init_tokio();
|
||||
/// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
|
||||
/// let act = Action::new(|n: &u8| {
|
||||
/// let n = n.to_owned();
|
||||
/// async move { n * 2 }
|
||||
/// });
|
||||
///
|
||||
/// let pending = act.pending();
|
||||
/// assert_eq!(pending.get(), false);
|
||||
/// act.dispatch(3);
|
||||
/// assert_eq!(pending.get(), true);
|
||||
///
|
||||
/// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;
|
||||
/// // after it resolves
|
||||
/// assert_eq!(pending.get(), false);
|
||||
/// # });
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn pending(&self) -> Memo<bool, SyncStorage> {
|
||||
let inner = self
|
||||
.inner
|
||||
.try_with_value(|inner| inner.pending())
|
||||
.unwrap_or_else(unwrap_signal!(self));
|
||||
inner.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O, S> Action<I, O, S>
|
||||
where
|
||||
I: 'static,
|
||||
S: Storage<ArcAction<I, O>> + Storage<ArcRwSignal<Option<I>>>,
|
||||
{
|
||||
/// The current argument that was dispatched to the async function. This value will
|
||||
/// be `Some` while we are waiting for it to resolve, and `None` after it has resolved.
|
||||
///
|
||||
|
@ -729,14 +806,20 @@ where
|
|||
/// # });
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn input(&self) -> RwSignal<Option<I>> {
|
||||
pub fn input(&self) -> RwSignal<Option<I>, S> {
|
||||
let inner = self
|
||||
.inner
|
||||
.try_with_value(|inner| inner.input())
|
||||
.unwrap_or_else(unwrap_signal!(self));
|
||||
inner.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O, S> Action<I, O, S>
|
||||
where
|
||||
O: 'static,
|
||||
S: Storage<ArcAction<I, O>> + Storage<ArcRwSignal<Option<O>>>,
|
||||
{
|
||||
/// The most recent return value of the `async` function. This will be `None` before
|
||||
/// the action has ever run successfully, and subsequently will always be `Some(_)`,
|
||||
/// holding the old value until a new value has been received.
|
||||
|
@ -766,59 +849,49 @@ where
|
|||
/// # });
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn value(&self) -> RwSignal<Option<O>> {
|
||||
pub fn value(&self) -> RwSignal<Option<O>, S> {
|
||||
let inner = self
|
||||
.inner
|
||||
.try_with_value(|inner| inner.value())
|
||||
.unwrap_or_else(unwrap_signal!(self));
|
||||
inner.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O, S> Action<I, O, S>
|
||||
where
|
||||
I: Send + Sync + 'static,
|
||||
O: Send + Sync + 'static,
|
||||
S: Storage<ArcAction<I, O>>,
|
||||
{
|
||||
/// Calls the `async` function with a reference to the input type as its argument.
|
||||
#[track_caller]
|
||||
pub fn dispatch(&self, input: I) {
|
||||
self.inner.with_value(|inner| inner.dispatch(input));
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the action has been dispatched and is currently waiting to resolve.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use reactive_graph::actions::*;
|
||||
/// # use reactive_graph::prelude::*;
|
||||
/// # tokio_test::block_on(async move {
|
||||
/// # any_spawner::Executor::init_tokio();
|
||||
/// # let _guard = reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
|
||||
/// let act = Action::new(|n: &u8| {
|
||||
/// let n = n.to_owned();
|
||||
/// async move { n * 2 }
|
||||
/// });
|
||||
///
|
||||
/// let pending = act.pending();
|
||||
/// assert_eq!(pending.get(), false);
|
||||
/// act.dispatch(3);
|
||||
/// assert_eq!(pending.get(), true);
|
||||
///
|
||||
/// # tokio::time::sleep(std::time::Duration::from_millis(10)).await;
|
||||
/// // after it resolves
|
||||
/// assert_eq!(pending.get(), false);
|
||||
/// # });
|
||||
/// ```
|
||||
impl<I, O, S> Action<I, O, S>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
S: Storage<ArcAction<I, O>>,
|
||||
{
|
||||
/// Calls the `async` function with a reference to the input type as its argument.
|
||||
#[track_caller]
|
||||
pub fn pending(&self) -> Memo<bool> {
|
||||
let inner = self
|
||||
.inner
|
||||
.try_with_value(|inner| inner.pending())
|
||||
.unwrap_or_else(unwrap_signal!(self));
|
||||
inner.into()
|
||||
pub fn dispatch_local(&self, input: I) {
|
||||
self.inner.with_value(|inner| inner.dispatch_local(input));
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> Action<I, O>
|
||||
impl<I, O, S> Action<I, O, S>
|
||||
where
|
||||
I: Send + Sync + 'static,
|
||||
O: Send + Sync + 'static,
|
||||
S: Storage<ArcAction<I, O>>,
|
||||
{
|
||||
/// Creates a new action, which does not require its input to be `Send`.
|
||||
/// Creates a new action, which does not require the action itself to be `Send`, but will run
|
||||
/// it on the same thread it was created on.
|
||||
///
|
||||
/// In all other ways, this is identical to [`Action::new`].
|
||||
#[track_caller]
|
||||
|
@ -828,14 +901,16 @@ where
|
|||
Fu: Future<Output = O> + 'static,
|
||||
{
|
||||
Self {
|
||||
inner: StoredValue::new(ArcAction::new_unsync(action_fn)),
|
||||
inner: StoredValue::new_with_storage(ArcAction::new_unsync(
|
||||
action_fn,
|
||||
)),
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new action, which does not require its input to be `Send`,
|
||||
/// initializing it with the given value.
|
||||
/// Creates a new action, which does not require the action itself to be `Send`, but will run
|
||||
/// it on the same thread it was created on, and gives an initial value.
|
||||
///
|
||||
/// In all other ways, this is identical to [`Action::new`].
|
||||
#[track_caller]
|
||||
|
@ -845,20 +920,16 @@ where
|
|||
Fu: Future<Output = O> + 'static,
|
||||
{
|
||||
Self {
|
||||
inner: StoredValue::new(ArcAction::new_unsync_with_value(
|
||||
value, action_fn,
|
||||
)),
|
||||
inner: StoredValue::new_with_storage(
|
||||
ArcAction::new_unsync_with_value(value, action_fn),
|
||||
),
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> DefinedAt for Action<I, O>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
impl<I, O, S> DefinedAt for Action<I, O, S> {
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -871,22 +942,13 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<I, O> Clone for Action<I, O>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
impl<I, O, S> Clone for Action<I, O, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> Copy for Action<I, O>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
}
|
||||
impl<I, O, S> Copy for Action<I, O, S> {}
|
||||
|
||||
/// Creates a new action. This is lazy: it does not run the action function until some value
|
||||
/// is dispatched.
|
||||
|
@ -933,7 +995,7 @@ where
|
|||
#[track_caller]
|
||||
#[deprecated = "This function is being removed to conform to Rust idioms. \
|
||||
Please use `Action::new()` instead."]
|
||||
pub fn create_action<I, O, F, Fu>(action_fn: F) -> Action<I, O>
|
||||
pub fn create_action<I, O, F, Fu>(action_fn: F) -> Action<I, O, SyncStorage>
|
||||
where
|
||||
I: Send + Sync + 'static,
|
||||
O: Send + Sync + 'static,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
diagnostics::is_suppressing_resource_load,
|
||||
owner::StoredValue,
|
||||
owner::{Storage, StoredValue, SyncStorage},
|
||||
signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},
|
||||
traits::{DefinedAt, Dispose, GetUntracked, Set, Update},
|
||||
unwrap_signal,
|
||||
|
@ -45,23 +45,19 @@ use std::{fmt::Debug, future::Future, panic::Location, pin::Pin, sync::Arc};
|
|||
/// assert_eq!(submissions.with(Vec::len), 3);
|
||||
/// # });
|
||||
/// ```
|
||||
pub struct MultiAction<I, O>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
inner: StoredValue<ArcMultiAction<I, O>>,
|
||||
pub struct MultiAction<I, O, S = SyncStorage> {
|
||||
inner: StoredValue<ArcMultiAction<I, O>, S>,
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
}
|
||||
|
||||
impl<I: 'static, O: 'static> Dispose for MultiAction<I, O> {
|
||||
impl<I, O, S> Dispose for MultiAction<I, O, S> {
|
||||
fn dispose(self) {
|
||||
self.inner.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> DefinedAt for MultiAction<I, O>
|
||||
impl<I, O, S> DefinedAt for MultiAction<I, O, S>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
|
@ -78,14 +74,14 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<I, O> Copy for MultiAction<I, O>
|
||||
impl<I, O, S> Copy for MultiAction<I, O, S>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
}
|
||||
|
||||
impl<I, O> Clone for MultiAction<I, O>
|
||||
impl<I, O, S> Clone for MultiAction<I, O, S>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
|
@ -95,7 +91,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<I, O> MultiAction<I, O>
|
||||
impl<I, O> MultiAction<I, O, SyncStorage>
|
||||
where
|
||||
I: Send + Sync + 'static,
|
||||
O: Send + Sync + 'static,
|
||||
|
@ -134,12 +130,21 @@ where
|
|||
Fut: Future<Output = O> + Send + 'static,
|
||||
{
|
||||
Self {
|
||||
inner: StoredValue::new(ArcMultiAction::new(action_fn)),
|
||||
inner: StoredValue::new_with_storage(ArcMultiAction::new(
|
||||
action_fn,
|
||||
)),
|
||||
#[cfg(debug_assertions)]
|
||||
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>>,
|
||||
{
|
||||
/// Calls the `async` function with a reference to the input type as its argument.
|
||||
///
|
||||
/// This can be called any number of times: each submission will be dispatched, running
|
||||
|
@ -230,7 +235,15 @@ where
|
|||
pub fn dispatch_sync(&self, value: O) {
|
||||
self.inner.with_value(|inner| inner.dispatch_sync(value));
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O, S> MultiAction<I, O, S>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
S: Storage<ArcMultiAction<I, O>>
|
||||
+ Storage<ArcReadSignal<Vec<ArcSubmission<I, O>>>>,
|
||||
{
|
||||
/// The set of all submissions to this multi-action.
|
||||
/// ```rust
|
||||
/// # use reactive_graph::actions::*;
|
||||
|
@ -257,13 +270,21 @@ where
|
|||
/// assert_eq!(submissions.with(Vec::len), 3);
|
||||
/// # });
|
||||
/// ```
|
||||
pub fn submissions(&self) -> ReadSignal<Vec<ArcSubmission<I, O>>> {
|
||||
pub fn submissions(&self) -> ReadSignal<Vec<ArcSubmission<I, O>>, S> {
|
||||
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>>>>,
|
||||
{
|
||||
/// How many times an action has successfully resolved.
|
||||
/// ```rust
|
||||
/// # use reactive_graph::actions::*;
|
||||
|
@ -294,7 +315,7 @@ where
|
|||
/// assert_eq!(version.get(), 3);
|
||||
/// # });
|
||||
/// ```
|
||||
pub fn version(&self) -> RwSignal<usize> {
|
||||
pub fn version(&self) -> RwSignal<usize, SyncStorage> {
|
||||
self.inner
|
||||
.try_with_value(|inner| inner.version())
|
||||
.unwrap_or_else(unwrap_signal!(self))
|
||||
|
@ -339,11 +360,7 @@ where
|
|||
/// assert_eq!(submissions.with(Vec::len), 3);
|
||||
/// # });
|
||||
/// ```
|
||||
pub struct ArcMultiAction<I, O>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
pub struct ArcMultiAction<I, O> {
|
||||
version: ArcRwSignal<usize>,
|
||||
submissions: ArcRwSignal<Vec<ArcSubmission<I, O>>>,
|
||||
#[allow(clippy::complexity)]
|
||||
|
@ -379,11 +396,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<I, O> ArcMultiAction<I, O>
|
||||
where
|
||||
I: Send + Sync + 'static,
|
||||
O: Send + Sync + 'static,
|
||||
{
|
||||
impl<I, O> ArcMultiAction<I, O> {
|
||||
/// Creates a new multi-action.
|
||||
///
|
||||
/// The input to the `async` function should always be a single value,
|
||||
|
@ -427,7 +440,13 @@ where
|
|||
action_fn,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> ArcMultiAction<I, O>
|
||||
where
|
||||
I: Send + Sync + 'static,
|
||||
O: Send + Sync + 'static,
|
||||
{
|
||||
/// Calls the `async` function with a reference to the input type as its argument.
|
||||
///
|
||||
/// This can be called any number of times: each submission will be dispatched, running
|
||||
|
@ -556,7 +575,9 @@ where
|
|||
.try_update(|subs| subs.push(submission.clone()));
|
||||
self.version.try_update(|n| *n += 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> ArcMultiAction<I, O> {
|
||||
/// The set of all submissions to this multi-action.
|
||||
/// ```rust
|
||||
/// # use reactive_graph::actions::*;
|
||||
|
@ -624,11 +645,7 @@ where
|
|||
|
||||
/// An action that has been submitted by dispatching it to a [`MultiAction`].
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ArcSubmission<I, O>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
pub struct ArcSubmission<I, O> {
|
||||
/// The current argument that was dispatched to the `async` function.
|
||||
/// `Some` while we are waiting for it to resolve, `None` if it has resolved.
|
||||
input: ArcRwSignal<Option<I>>,
|
||||
|
@ -692,25 +709,26 @@ impl<I, O> Clone for ArcSubmission<I, O> {
|
|||
|
||||
/// An action that has been submitted by dispatching it to a [`MultiAction`].
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Submission<I, O>
|
||||
pub struct Submission<I, O, S = SyncStorage>
|
||||
where
|
||||
I: 'static,
|
||||
O: 'static,
|
||||
{
|
||||
/// The current argument that was dispatched to the `async` function.
|
||||
/// `Some` while we are waiting for it to resolve, `None` if it has resolved.
|
||||
input: RwSignal<Option<I>>,
|
||||
input: RwSignal<Option<I>, S>,
|
||||
/// The most recent return value of the `async` function.
|
||||
value: RwSignal<Option<O>>,
|
||||
pending: RwSignal<bool>,
|
||||
value: RwSignal<Option<O>, S>,
|
||||
pending: RwSignal<bool, SyncStorage>,
|
||||
/// Controls this submission has been canceled.
|
||||
canceled: RwSignal<bool>,
|
||||
canceled: RwSignal<bool, SyncStorage>,
|
||||
}
|
||||
|
||||
impl<I, O> From<ArcSubmission<I, O>> for Submission<I, O>
|
||||
impl<I, O, S> From<ArcSubmission<I, O>> for Submission<I, O, S>
|
||||
where
|
||||
I: Send + Sync + 'static,
|
||||
O: Send + Sync + 'static,
|
||||
S: Storage<ArcRwSignal<Option<I>>> + Storage<ArcRwSignal<Option<O>>>,
|
||||
{
|
||||
fn from(value: ArcSubmission<I, O>) -> Self {
|
||||
let ArcSubmission {
|
||||
|
@ -728,33 +746,39 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<I, O> Submission<I, O>
|
||||
impl<I, O, S> Submission<I, O, S>
|
||||
where
|
||||
I: Send + Sync + 'static,
|
||||
O: Send + Sync + 'static,
|
||||
S: Storage<ArcRwSignal<Option<I>>> + Storage<ArcReadSignal<Option<I>>>,
|
||||
{
|
||||
/// The current argument that was dispatched to the `async` function.
|
||||
/// `Some` while we are waiting for it to resolve, `None` if it has resolved.
|
||||
#[track_caller]
|
||||
pub fn input(&self) -> ReadSignal<Option<I>> {
|
||||
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>>>,
|
||||
{
|
||||
/// The most recent return value of the `async` function.
|
||||
#[track_caller]
|
||||
pub fn value(&self) -> ReadSignal<Option<O>> {
|
||||
pub fn value(&self) -> ReadSignal<Option<O>, S> {
|
||||
self.value.read_only()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O, S> Submission<I, O, S> {
|
||||
/// Whether this submision is still waiting to resolve.
|
||||
#[track_caller]
|
||||
pub fn pending(&self) -> ReadSignal<bool> {
|
||||
pub fn pending(&self) -> ReadSignal<bool, SyncStorage> {
|
||||
self.pending.read_only()
|
||||
}
|
||||
|
||||
/// Whether this submission has been canceled.
|
||||
#[track_caller]
|
||||
pub fn canceled(&self) -> ReadSignal<bool> {
|
||||
pub fn canceled(&self) -> ReadSignal<bool, SyncStorage> {
|
||||
self.canceled.read_only()
|
||||
}
|
||||
|
||||
|
@ -766,10 +790,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<I, O> Clone for Submission<I, O> {
|
||||
impl<I, O, S> Clone for Submission<I, O, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> Copy for Submission<I, O> {}
|
||||
impl<I, O, S> Copy for Submission<I, O, S> {}
|
||||
|
|
|
@ -409,7 +409,7 @@ impl<T: 'static> ArcAsyncDerived<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> ReadUntracked for ArcAsyncDerived<T> {
|
||||
impl<T: 'static> ReadUntracked for ArcAsyncDerived<T> {
|
||||
type Value = ReadGuard<Option<T>, AsyncPlain<Option<T>>>;
|
||||
|
||||
fn try_read_untracked(&self) -> Option<Self::Value> {
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
AnySource, AnySubscriber, ReactiveNode, Source, Subscriber,
|
||||
ToAnySource, ToAnySubscriber,
|
||||
},
|
||||
owner::StoredValue,
|
||||
owner::{LocalStorage, Storage, StoredValue, SyncStorage},
|
||||
signal::guards::{AsyncPlain, ReadGuard},
|
||||
traits::{DefinedAt, Dispose, ReadUntracked},
|
||||
unwrap_signal,
|
||||
|
@ -78,31 +78,38 @@ use std::{future::Future, panic::Location};
|
|||
/// tracking it.
|
||||
/// - [`IntoFuture`](std::future::Future) allows you to create a [`Future`] that resolves
|
||||
/// when this resource is done loading.
|
||||
pub struct AsyncDerived<T> {
|
||||
pub struct AsyncDerived<T, S = SyncStorage> {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
pub(crate) inner: StoredValue<ArcAsyncDerived<T>>,
|
||||
pub(crate) inner: StoredValue<ArcAsyncDerived<T>, S>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Dispose for AsyncDerived<T> {
|
||||
impl<T, S> Dispose for AsyncDerived<T, S> {
|
||||
fn dispose(self) {
|
||||
self.inner.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> From<ArcAsyncDerived<T>> for AsyncDerived<T> {
|
||||
impl<T, S> From<ArcAsyncDerived<T>> for AsyncDerived<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcAsyncDerived<T>>,
|
||||
{
|
||||
fn from(value: ArcAsyncDerived<T>) -> Self {
|
||||
#[cfg(debug_assertions)]
|
||||
let defined_at = value.defined_at;
|
||||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at,
|
||||
inner: StoredValue::new(value),
|
||||
inner: StoredValue::new_with_storage(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> AsyncDerived<T> {
|
||||
impl<T> AsyncDerived<T, SyncStorage>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
/// Creates a new async derived computation.
|
||||
///
|
||||
/// This runs eagerly: i.e., calls `fun` once when created and immediately spawns the `Future`
|
||||
|
@ -116,7 +123,7 @@ impl<T: Send + Sync + 'static> AsyncDerived<T> {
|
|||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(ArcAsyncDerived::new(fun)),
|
||||
inner: StoredValue::new_with_storage(ArcAsyncDerived::new(fun)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,13 +141,17 @@ impl<T: Send + Sync + 'static> AsyncDerived<T> {
|
|||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(ArcAsyncDerived::new_with_initial(
|
||||
initial_value,
|
||||
fun,
|
||||
)),
|
||||
inner: StoredValue::new_with_storage(
|
||||
ArcAsyncDerived::new_with_initial(initial_value, fun),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsyncDerived<T, LocalStorage>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
/// Creates a new async derived computation that will be guaranteed to run on the current
|
||||
/// thread.
|
||||
///
|
||||
|
@ -154,7 +165,9 @@ impl<T: Send + Sync + 'static> AsyncDerived<T> {
|
|||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(ArcAsyncDerived::new_unsync(fun)),
|
||||
inner: StoredValue::new_with_storage(ArcAsyncDerived::new_unsync(
|
||||
fun,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,30 +186,41 @@ impl<T: Send + Sync + 'static> AsyncDerived<T> {
|
|||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(ArcAsyncDerived::new_unsync_with_initial(
|
||||
initial_value,
|
||||
fun,
|
||||
)),
|
||||
inner: StoredValue::new_with_storage(
|
||||
ArcAsyncDerived::new_unsync_with_initial(initial_value, fun),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> AsyncDerived<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcAsyncDerived<T>>,
|
||||
{
|
||||
/// Returns a `Future` that is ready when this resource has next finished loading.
|
||||
#[track_caller]
|
||||
pub fn ready(&self) -> AsyncDerivedReadyFuture {
|
||||
let this = self.inner.get().unwrap_or_else(unwrap_signal!(self));
|
||||
let this = self
|
||||
.inner
|
||||
.try_get_value()
|
||||
.unwrap_or_else(unwrap_signal!(self));
|
||||
this.ready()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for AsyncDerived<T> {}
|
||||
impl<T, S> Copy for AsyncDerived<T, S> {}
|
||||
|
||||
impl<T> Clone for AsyncDerived<T> {
|
||||
impl<T, S> Clone for AsyncDerived<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for AsyncDerived<T> {
|
||||
impl<T, S> Debug for AsyncDerived<T, S>
|
||||
where
|
||||
S: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AsyncDerived")
|
||||
.field("type", &std::any::type_name::<T>())
|
||||
|
@ -205,7 +229,7 @@ impl<T> Debug for AsyncDerived<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> DefinedAt for AsyncDerived<T> {
|
||||
impl<T, S> DefinedAt for AsyncDerived<T, S> {
|
||||
#[inline(always)]
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -219,73 +243,95 @@ impl<T> DefinedAt for AsyncDerived<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> ReadUntracked for AsyncDerived<T> {
|
||||
impl<T, S> ReadUntracked for AsyncDerived<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcAsyncDerived<T>>,
|
||||
{
|
||||
type Value = ReadGuard<Option<T>, AsyncPlain<Option<T>>>;
|
||||
|
||||
fn try_read_untracked(&self) -> Option<Self::Value> {
|
||||
self.inner.get().map(|inner| inner.read_untracked())
|
||||
self.inner
|
||||
.try_get_value()
|
||||
.map(|inner| inner.read_untracked())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> ToAnySource for AsyncDerived<T> {
|
||||
impl<T, S> ToAnySource for AsyncDerived<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcAsyncDerived<T>>,
|
||||
{
|
||||
fn to_any_source(&self) -> AnySource {
|
||||
self.inner
|
||||
.get()
|
||||
.try_get_value()
|
||||
.map(|inner| inner.to_any_source())
|
||||
.unwrap_or_else(unwrap_signal!(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> ToAnySubscriber for AsyncDerived<T> {
|
||||
impl<T, S> ToAnySubscriber for AsyncDerived<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcAsyncDerived<T>>,
|
||||
{
|
||||
fn to_any_subscriber(&self) -> AnySubscriber {
|
||||
self.inner
|
||||
.get()
|
||||
.try_get_value()
|
||||
.map(|inner| inner.to_any_subscriber())
|
||||
.unwrap_or_else(unwrap_signal!(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> Source for AsyncDerived<T> {
|
||||
impl<T, S> Source for AsyncDerived<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcAsyncDerived<T>>,
|
||||
{
|
||||
fn add_subscriber(&self, subscriber: AnySubscriber) {
|
||||
if let Some(inner) = self.inner.get() {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.add_subscriber(subscriber);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_subscriber(&self, subscriber: &AnySubscriber) {
|
||||
if let Some(inner) = self.inner.get() {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.remove_subscriber(subscriber);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_subscribers(&self) {
|
||||
if let Some(inner) = self.inner.get() {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.clear_subscribers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> ReactiveNode for AsyncDerived<T> {
|
||||
impl<T, S> ReactiveNode for AsyncDerived<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcAsyncDerived<T>>,
|
||||
{
|
||||
fn mark_dirty(&self) {
|
||||
if let Some(inner) = self.inner.get() {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.mark_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_check(&self) {
|
||||
if let Some(inner) = self.inner.get() {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.mark_check();
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_subscribers_check(&self) {
|
||||
if let Some(inner) = self.inner.get() {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.mark_subscribers_check();
|
||||
}
|
||||
}
|
||||
|
||||
fn update_if_necessary(&self) -> bool {
|
||||
if let Some(inner) = self.inner.get() {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.update_if_necessary()
|
||||
} else {
|
||||
false
|
||||
|
@ -293,15 +339,19 @@ impl<T: Send + Sync + 'static> ReactiveNode for AsyncDerived<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> Subscriber for AsyncDerived<T> {
|
||||
impl<T, S> Subscriber for AsyncDerived<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcAsyncDerived<T>>,
|
||||
{
|
||||
fn add_source(&self, source: AnySource) {
|
||||
if let Some(inner) = self.inner.get() {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.add_source(source);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_sources(&self, subscriber: &AnySubscriber) {
|
||||
if let Some(inner) = self.inner.get() {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.clear_sources(subscriber);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::{ArcAsyncDerived, AsyncDerived};
|
||||
use crate::{
|
||||
graph::{AnySource, ToAnySource},
|
||||
owner::Storage,
|
||||
signal::guards::{AsyncPlain, Mapped, ReadGuard},
|
||||
traits::{DefinedAt, Track},
|
||||
unwrap_signal,
|
||||
|
@ -63,16 +64,20 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> IntoFuture for AsyncDerived<T>
|
||||
impl<T, S> IntoFuture for AsyncDerived<T, S>
|
||||
where
|
||||
T: Clone + 'static,
|
||||
S: Storage<ArcAsyncDerived<T>>,
|
||||
{
|
||||
type Output = T;
|
||||
type IntoFuture = AsyncDerivedFuture<T>;
|
||||
|
||||
#[track_caller]
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
let this = self.inner.get().unwrap_or_else(unwrap_signal!(self));
|
||||
let this = self
|
||||
.inner
|
||||
.try_get_value()
|
||||
.unwrap_or_else(unwrap_signal!(self));
|
||||
this.into_future()
|
||||
}
|
||||
}
|
||||
|
@ -125,12 +130,19 @@ impl<T: 'static> ArcAsyncDerived<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> AsyncDerived<T> {
|
||||
impl<T, S> AsyncDerived<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcAsyncDerived<T>>,
|
||||
{
|
||||
/// Returns a `Future` that resolves when the computation is finished, and accesses the inner
|
||||
/// value by reference rather than by cloning it.
|
||||
#[track_caller]
|
||||
pub fn by_ref(&self) -> AsyncDerivedRefFuture<T> {
|
||||
let this = self.inner.get().unwrap_or_else(unwrap_signal!(self));
|
||||
let this = self
|
||||
.inner
|
||||
.try_get_value()
|
||||
.unwrap_or_else(unwrap_signal!(self));
|
||||
this.by_ref()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::{inner::MemoInner, ArcMemo};
|
||||
use crate::{
|
||||
owner::StoredValue,
|
||||
owner::{Storage, StoredValue, SyncStorage},
|
||||
signal::{
|
||||
guards::{Mapped, Plain, ReadGuard},
|
||||
ArcReadSignal,
|
||||
|
@ -96,30 +96,37 @@ use std::{fmt::Debug, hash::Hash, panic::Location};
|
|||
/// stream of values.
|
||||
/// - [`::from_stream()`](crate::traits::FromStream) converts an `async` stream
|
||||
/// of values into a memo containing the latest value.
|
||||
pub struct Memo<T> {
|
||||
pub struct Memo<T, S = SyncStorage> {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
inner: StoredValue<ArcMemo<T>>,
|
||||
inner: StoredValue<ArcMemo<T>, S>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Dispose for Memo<T> {
|
||||
impl<T, S> Dispose for Memo<T, S> {
|
||||
fn dispose(self) {
|
||||
self.inner.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> From<ArcMemo<T>> for Memo<T> {
|
||||
impl<T, S> From<ArcMemo<T>> for Memo<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcMemo<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: ArcMemo<T>) -> Self {
|
||||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(value),
|
||||
inner: StoredValue::new_with_storage(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> Memo<T> {
|
||||
impl<T> Memo<T, SyncStorage>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
{
|
||||
#[track_caller]
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
|
@ -151,7 +158,7 @@ impl<T: Send + Sync + 'static> Memo<T> {
|
|||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(ArcMemo::new(fun)),
|
||||
inner: StoredValue::new_with_storage(ArcMemo::new(fun)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,7 +183,9 @@ impl<T: Send + Sync + 'static> Memo<T> {
|
|||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(ArcMemo::new_with_compare(fun, changed)),
|
||||
inner: StoredValue::new_with_storage(ArcMemo::new_with_compare(
|
||||
fun, changed,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,20 +210,22 @@ impl<T: Send + Sync + 'static> Memo<T> {
|
|||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(ArcMemo::new_owning(fun)),
|
||||
inner: StoredValue::new_with_storage(ArcMemo::new_owning(fun)),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T, S> Copy for Memo<T, S> {}
|
||||
|
||||
impl<T> Copy for Memo<T> {}
|
||||
|
||||
impl<T> Clone for Memo<T> {
|
||||
impl<T, S> Clone for Memo<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for Memo<T> {
|
||||
impl<T, S> Debug for Memo<T, S>
|
||||
where
|
||||
S: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Memo")
|
||||
.field("type", &std::any::type_name::<T>())
|
||||
|
@ -223,21 +234,21 @@ impl<T> Debug for Memo<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Memo<T> {
|
||||
impl<T, S> PartialEq for Memo<T, S> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Memo<T> {}
|
||||
impl<T, S> Eq for Memo<T, S> {}
|
||||
|
||||
impl<T> Hash for Memo<T> {
|
||||
impl<T, S> Hash for Memo<T, S> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.inner.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DefinedAt for Memo<T> {
|
||||
impl<T, S> DefinedAt for Memo<T, S> {
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -250,31 +261,49 @@ impl<T> DefinedAt for Memo<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> Track for Memo<T> {
|
||||
impl<T, S> Track for Memo<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcMemo<T>>,
|
||||
ArcMemo<T>: Track,
|
||||
{
|
||||
#[track_caller]
|
||||
fn track(&self) {
|
||||
if let Some(inner) = self.inner.get() {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.track();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> ReadUntracked for Memo<T> {
|
||||
impl<T, S> ReadUntracked for Memo<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcMemo<T>>,
|
||||
{
|
||||
type Value = ReadGuard<T, Mapped<Plain<MemoInner<T>>, T>>;
|
||||
|
||||
fn try_read_untracked(&self) -> Option<Self::Value> {
|
||||
self.inner.get().map(|inner| inner.read_untracked())
|
||||
self.inner
|
||||
.try_get_value()
|
||||
.map(|inner| inner.read_untracked())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> From<Memo<T>> for ArcMemo<T> {
|
||||
impl<T, S> From<Memo<T, S>> for ArcMemo<T>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcMemo<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: Memo<T>) -> Self {
|
||||
value.inner.get().unwrap_or_else(unwrap_signal!(value))
|
||||
fn from(value: Memo<T, S>) -> Self {
|
||||
value
|
||||
.inner
|
||||
.try_get_value()
|
||||
.unwrap_or_else(unwrap_signal!(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<ArcReadSignal<T>> for Memo<T>
|
||||
impl<T> From<ArcReadSignal<T>> for Memo<T, SyncStorage>
|
||||
where
|
||||
T: Clone + PartialEq + Send + Sync + 'static,
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
AnySubscriber, ReactiveNode, SourceSet, Subscriber, ToAnySubscriber,
|
||||
WithObserver,
|
||||
},
|
||||
owner::{Owner, StoredValue},
|
||||
owner::{LocalStorage, Owner, StoredValue},
|
||||
traits::Dispose,
|
||||
};
|
||||
use any_spawner::Executor;
|
||||
|
@ -73,7 +73,7 @@ use std::{
|
|||
/// and you can call browser-specific APIs within the effect function without causing issues.
|
||||
/// If you need an effect to run on the server, use [`Effect::new_isomorphic`].
|
||||
pub struct Effect {
|
||||
inner: StoredValue<Option<Arc<RwLock<EffectInner>>>>,
|
||||
inner: StoredValue<Option<Arc<RwLock<EffectInner>>>, LocalStorage>,
|
||||
}
|
||||
|
||||
impl Dispose for Effect {
|
||||
|
@ -148,7 +148,7 @@ impl Effect {
|
|||
}
|
||||
|
||||
Self {
|
||||
inner: StoredValue::new(Some(inner)),
|
||||
inner: StoredValue::new_with_storage(Some(inner)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ impl Effect {
|
|||
}
|
||||
|
||||
Self {
|
||||
inner: StoredValue::new(Some(inner)),
|
||||
inner: StoredValue::new_with_storage(Some(inner)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,7 +237,7 @@ impl Effect {
|
|||
}
|
||||
});
|
||||
Self {
|
||||
inner: StoredValue::new(Some(inner)),
|
||||
inner: StoredValue::new_with_storage(Some(inner)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,132 +6,147 @@ use crate::{
|
|||
},
|
||||
traits::{Get, Set},
|
||||
wrappers::{
|
||||
read::{ArcSignal, Signal},
|
||||
read::{ArcSignal, MaybeProp, MaybeSignal, Signal},
|
||||
write::SignalSetter,
|
||||
},
|
||||
};
|
||||
|
||||
macro_rules! impl_set_fn_traits {
|
||||
($($ty:ident $($method_name:ident)?),*) => {
|
||||
($($ty:ident),*) => {
|
||||
$(
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<T: 'static> FnOnce<(T,)> for $ty<T> {
|
||||
impl<T> FnOnce<(T,)> for $ty<T> where $ty<T>: Set<Value = T> {
|
||||
type Output = ();
|
||||
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call_once(self, args: (T,)) -> Self::Output {
|
||||
impl_set_fn_traits!(@method_name self $($method_name)? args)
|
||||
self.set(args.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<T: 'static> FnMut<(T,)> for $ty<T> {
|
||||
impl<T> FnMut<(T,)> for $ty<T> where $ty<T>: Set<Value = T> {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call_mut(&mut self, args: (T,)) -> Self::Output {
|
||||
impl_set_fn_traits!(@method_name self $($method_name)? args)
|
||||
self.set(args.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<T: 'static> Fn<(T,)> for $ty<T> {
|
||||
impl<T> Fn<(T,)> for $ty<T> where $ty<T>: Set<Value = T> {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call(&self, args: (T,)) -> Self::Output {
|
||||
impl_set_fn_traits!(@method_name self $($method_name)? args)
|
||||
self.set(args.0);
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
(@method_name $self:ident $args:ident) => {
|
||||
$self.set($args.0)
|
||||
};
|
||||
(@method_name $self:ident $ident:ident $args:ident) => {
|
||||
$self.$ident($args.0)
|
||||
}
|
||||
|
||||
macro_rules! impl_set_fn_traits_arena {
|
||||
($($ty:ident),*) => {
|
||||
$(
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<T, S> FnOnce<(T,)> for $ty<T, S> where $ty<T, S>: Set<Value = T> {
|
||||
type Output = ();
|
||||
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call_once(self, args: (T,)) -> Self::Output {
|
||||
self.set(args.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<T, S> FnMut<(T,)> for $ty<T, S> where $ty<T, S>: Set<Value = T> {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call_mut(&mut self, args: (T,)) -> Self::Output {
|
||||
self.set(args.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<T, S> Fn<(T,)> for $ty<T, S> where $ty<T, S>: Set<Value = T> {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call(&self, args: (T,)) -> Self::Output {
|
||||
self.set(args.0);
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_get_fn_traits_get {
|
||||
($($ty:ident $(($method_name:ident))?),*) => {
|
||||
($($ty:ident),*) => {
|
||||
$(
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<T: Clone + 'static> FnOnce<()> for $ty<T> {
|
||||
impl<T> FnOnce<()> for $ty<T> where $ty<T>: Get {
|
||||
type Output = <Self as Get>::Value;
|
||||
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
|
||||
impl_get_fn_traits_get_send!(@method_name self $($method_name)?)
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<T: Clone + 'static> FnMut<()> for $ty<T> {
|
||||
impl<T> FnMut<()> for $ty<T> where $ty<T>: Get {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
||||
impl_get_fn_traits_get_send!(@method_name self $($method_name)?)
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<T: Clone + 'static> Fn<()> for $ty<T> {
|
||||
impl<T> Fn<()> for $ty<T> where $ty<T>: Get {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
||||
impl_get_fn_traits_get_send!(@method_name self $($method_name)?)
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
(@method_name $self:ident) => {
|
||||
$self.get()
|
||||
};
|
||||
(@method_name $self:ident $ident:ident) => {
|
||||
$self.$ident()
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_get_fn_traits_get_send {
|
||||
($($ty:ident $(($method_name:ident))?),*) => {
|
||||
macro_rules! impl_get_fn_traits_get_arena {
|
||||
($($ty:ident),*) => {
|
||||
$(
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<T: Send + Sync + Clone + 'static> FnOnce<()> for $ty<T> {
|
||||
impl<T, S> FnOnce<()> for $ty<T, S> where $ty<T, S>: Get {
|
||||
type Output = <Self as Get>::Value;
|
||||
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
|
||||
impl_get_fn_traits_get_send!(@method_name self $($method_name)?)
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<T: Send + Sync + Clone + 'static> FnMut<()> for $ty<T> {
|
||||
impl<T, S> FnMut<()> for $ty<T, S> where $ty<T, S>: Get {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
||||
impl_get_fn_traits_get_send!(@method_name self $($method_name)?)
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<T: Send + Sync + Clone + 'static> Fn<()> for $ty<T> {
|
||||
impl<T, S> Fn<()> for $ty<T, S> where $ty<T, S>: Get {
|
||||
#[inline(always)]
|
||||
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
||||
impl_get_fn_traits_get_send!(@method_name self $($method_name)?)
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
(@method_name $self:ident) => {
|
||||
$self.get()
|
||||
};
|
||||
(@method_name $self:ident $ident:ident) => {
|
||||
$self.$ident()
|
||||
};
|
||||
}
|
||||
|
||||
impl_get_fn_traits_get![ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal];
|
||||
impl_get_fn_traits_get_send![ArcSignal, Signal];
|
||||
impl_get_fn_traits_get_send![Memo, ArcMemo];
|
||||
impl_set_fn_traits![
|
||||
ArcRwSignal,
|
||||
ArcWriteSignal,
|
||||
impl_get_fn_traits_get![ArcReadSignal, ArcRwSignal, ArcMemo, ArcSignal];
|
||||
impl_get_fn_traits_get_arena![
|
||||
ReadSignal,
|
||||
RwSignal,
|
||||
WriteSignal,
|
||||
SignalSetter
|
||||
Memo,
|
||||
Signal,
|
||||
MaybeSignal,
|
||||
MaybeProp
|
||||
];
|
||||
impl_set_fn_traits![ArcRwSignal, ArcWriteSignal];
|
||||
impl_set_fn_traits_arena![RwSignal, WriteSignal, SignalSetter];
|
||||
|
|
|
@ -21,7 +21,9 @@ pub use arena::sandboxed::Sandboxed;
|
|||
use arena::NodeId;
|
||||
pub use context::*;
|
||||
#[allow(deprecated)] // allow exporting deprecated fn
|
||||
pub use stored_value::{store_value, StoredValue};
|
||||
pub use stored_value::{
|
||||
store_value, LocalStorage, Storage, StoredValue, SyncStorage,
|
||||
};
|
||||
|
||||
/// A reactive owner, which manages
|
||||
/// 1) the cancelation of [`Effect`](crate::effect::Effect)s,
|
||||
|
|
|
@ -6,7 +6,10 @@ use std::{any::Any, hash::Hash, sync::RwLock};
|
|||
#[cfg(feature = "sandboxed-arenas")]
|
||||
use std::{cell::RefCell, sync::Arc};
|
||||
|
||||
new_key_type! { pub(crate) struct NodeId; }
|
||||
new_key_type! {
|
||||
/// Unique identifier for an item stored in the arena.
|
||||
pub struct NodeId;
|
||||
}
|
||||
|
||||
pub(crate) struct Arena;
|
||||
|
||||
|
|
|
@ -6,8 +6,129 @@ use crate::{
|
|||
traits::{DefinedAt, Dispose, IsDisposed},
|
||||
unwrap_signal,
|
||||
};
|
||||
use send_wrapper::SendWrapper;
|
||||
use std::{any::Any, hash::Hash, marker::PhantomData, panic::Location};
|
||||
|
||||
/// A way of storing a [`StoredValue`], either as itself or with a wrapper to make it threadsafe.
|
||||
///
|
||||
/// This exists because all items stored in the arena must be `Send + Sync`, but in single-threaded
|
||||
/// environments you might want or need to use thread-unsafe types.
|
||||
pub trait Storage<T>: Send + Sync + 'static {
|
||||
/// The type being stored, once it has been wrapped.
|
||||
type Wrapped: Send + Sync + 'static;
|
||||
|
||||
/// Adds any needed wrapper to the type.
|
||||
fn wrap(value: T) -> Self::Wrapped;
|
||||
|
||||
/// Applies the given function to the stored value, if it exists and can be accessed from this
|
||||
/// thread.
|
||||
fn try_with<U>(node: NodeId, fun: impl FnOnce(&T) -> U) -> Option<U>;
|
||||
|
||||
/// Applies the given function to a mutable reference to the stored value, if it exists and can be accessed from this
|
||||
/// thread.
|
||||
fn try_with_mut<U>(
|
||||
node: NodeId,
|
||||
fun: impl FnOnce(&mut T) -> U,
|
||||
) -> Option<U>;
|
||||
|
||||
/// Sets a new value for the stored value. If it has been disposed, returns `Some(T)`.
|
||||
fn try_set(node: NodeId, value: T) -> Option<T>;
|
||||
}
|
||||
|
||||
/// A form of [`Storage`] that stores the type as itself, with no wrapper.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct SyncStorage;
|
||||
|
||||
impl<T> Storage<T> for SyncStorage
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
{
|
||||
type Wrapped = T;
|
||||
|
||||
#[inline(always)]
|
||||
fn wrap(value: T) -> Self::Wrapped {
|
||||
value
|
||||
}
|
||||
|
||||
fn try_with<U>(node: NodeId, fun: impl FnOnce(&T) -> U) -> Option<U> {
|
||||
Arena::with(|arena| {
|
||||
let m = arena.get(node);
|
||||
m.and_then(|n| n.downcast_ref::<T>()).map(fun)
|
||||
})
|
||||
}
|
||||
|
||||
fn try_with_mut<U>(
|
||||
node: NodeId,
|
||||
fun: impl FnOnce(&mut T) -> U,
|
||||
) -> Option<U> {
|
||||
Arena::with_mut(|arena| {
|
||||
let m = arena.get_mut(node);
|
||||
m.and_then(|n| n.downcast_mut::<T>()).map(fun)
|
||||
})
|
||||
}
|
||||
|
||||
fn try_set(node: NodeId, value: T) -> Option<T> {
|
||||
Arena::with_mut(|arena| {
|
||||
let m = arena.get_mut(node);
|
||||
match m.and_then(|n| n.downcast_mut::<T>()) {
|
||||
Some(inner) => {
|
||||
*inner = value;
|
||||
None
|
||||
}
|
||||
None => Some(value),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A form of [`Storage`] that stores the type with a wrapper that makes it `Send + Sync`, but only
|
||||
/// allows it to be accessed from the thread on which it was created.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct LocalStorage;
|
||||
|
||||
impl<T> Storage<T> for LocalStorage
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
type Wrapped = SendWrapper<T>;
|
||||
|
||||
fn wrap(value: T) -> Self::Wrapped {
|
||||
SendWrapper::new(value)
|
||||
}
|
||||
|
||||
fn try_with<U>(node: NodeId, fun: impl FnOnce(&T) -> U) -> Option<U> {
|
||||
Arena::with(|arena| {
|
||||
let m = arena.get(node);
|
||||
m.and_then(|n| n.downcast_ref::<SendWrapper<T>>())
|
||||
.map(|inner| fun(&inner))
|
||||
})
|
||||
}
|
||||
|
||||
fn try_with_mut<U>(
|
||||
node: NodeId,
|
||||
fun: impl FnOnce(&mut T) -> U,
|
||||
) -> Option<U> {
|
||||
Arena::with_mut(|arena| {
|
||||
let m = arena.get_mut(node);
|
||||
m.and_then(|n| n.downcast_mut::<SendWrapper<T>>())
|
||||
.map(|inner| fun(&mut *inner))
|
||||
})
|
||||
}
|
||||
|
||||
fn try_set(node: NodeId, value: T) -> Option<T> {
|
||||
Arena::with_mut(|arena| {
|
||||
let m = arena.get_mut(node);
|
||||
match m.and_then(|n| n.downcast_mut::<SendWrapper<T>>()) {
|
||||
Some(inner) => {
|
||||
*inner = SendWrapper::new(value);
|
||||
None
|
||||
}
|
||||
None => Some(value),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A **non-reactive**, `Copy` handle for any value.
|
||||
///
|
||||
/// This allows you to create a stable reference for any value by storing it within
|
||||
|
@ -16,37 +137,36 @@ use std::{any::Any, hash::Hash, marker::PhantomData, panic::Location};
|
|||
/// types, it is not reactive; accessing it does not cause effects to subscribe, and
|
||||
/// updating it does not notify anything else.
|
||||
#[derive(Debug)]
|
||||
pub struct StoredValue<T> {
|
||||
pub struct StoredValue<T, S = SyncStorage> {
|
||||
node: NodeId,
|
||||
ty: PhantomData<T>,
|
||||
ty: PhantomData<(SendWrapper<T>, S)>,
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
}
|
||||
|
||||
impl<T> Copy for StoredValue<T> {}
|
||||
impl<T, S> Copy for StoredValue<T, S> {}
|
||||
|
||||
impl<T> Clone for StoredValue<T> {
|
||||
impl<T, S> Clone for StoredValue<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for StoredValue<T> {
|
||||
impl<T, S> PartialEq for StoredValue<T, S> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.node == other.node && self.ty == other.ty
|
||||
self.node == other.node
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for StoredValue<T> {}
|
||||
impl<T, S> Eq for StoredValue<T, S> {}
|
||||
|
||||
impl<T> Hash for StoredValue<T> {
|
||||
impl<T, S> Hash for StoredValue<T, S> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.node.hash(state);
|
||||
self.ty.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DefinedAt for StoredValue<T> {
|
||||
impl<T, S> DefinedAt for StoredValue<T, S> {
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -59,16 +179,19 @@ impl<T> DefinedAt for StoredValue<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> StoredValue<T>
|
||||
impl<T, S> StoredValue<T, S>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
T: 'static,
|
||||
S: Storage<T>,
|
||||
{
|
||||
/// Stores the given value in the arena allocator.
|
||||
#[track_caller]
|
||||
pub fn new(value: T) -> Self {
|
||||
pub fn new_with_storage(value: T) -> Self {
|
||||
let node = {
|
||||
Arena::with_mut(|arena| {
|
||||
arena.insert(Box::new(value) as Box<dyn Any + Send + Sync>)
|
||||
arena.insert(
|
||||
Box::new(S::wrap(value)) as Box<dyn Any + Send + Sync>
|
||||
)
|
||||
})
|
||||
};
|
||||
OWNER.with(|o| {
|
||||
|
@ -86,14 +209,33 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> StoredValue<T> {
|
||||
impl<T> StoredValue<T, SyncStorage>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
{
|
||||
/// Stores the given value in the arena allocator.
|
||||
#[track_caller]
|
||||
pub fn new(value: T) -> Self {
|
||||
StoredValue::new_with_storage(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> StoredValue<T, LocalStorage>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
/// Stores the given value in the arena allocator.
|
||||
#[track_caller]
|
||||
pub fn new_local(value: T) -> Self {
|
||||
StoredValue::new_with_storage(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S: Storage<T>> StoredValue<T, S> {
|
||||
/// Same as [`StoredValue::with_value`] but returns `Some(O)` only if
|
||||
/// the stored value has not yet been disposed, `None` otherwise.
|
||||
pub fn try_with_value<U>(&self, fun: impl FnOnce(&T) -> U) -> Option<U> {
|
||||
Arena::with(|arena| {
|
||||
let m = arena.get(self.node);
|
||||
m.and_then(|n| n.downcast_ref::<T>()).map(fun)
|
||||
})
|
||||
S::try_with(self.node, fun)
|
||||
}
|
||||
|
||||
/// Applies a function to the current stored value and returns the result.
|
||||
|
@ -122,10 +264,7 @@ impl<T: 'static> StoredValue<T> {
|
|||
&self,
|
||||
fun: impl FnOnce(&mut T) -> U,
|
||||
) -> Option<U> {
|
||||
Arena::with_mut(|arena| {
|
||||
let m = arena.get_mut(self.node);
|
||||
m.and_then(|n| n.downcast_mut::<T>()).map(fun)
|
||||
})
|
||||
S::try_with_mut(self.node, fun)
|
||||
}
|
||||
|
||||
/// Updates the stored value by applying the given closure.
|
||||
|
@ -146,39 +285,22 @@ impl<T: 'static> StoredValue<T> {
|
|||
|
||||
/// Tries to set the value. If the value has been disposed, returns `Some(value)`.
|
||||
pub fn try_set_value(&self, value: T) -> Option<T> {
|
||||
Arena::with_mut(|arena| {
|
||||
let m = arena.get_mut(self.node);
|
||||
match m.and_then(|n| n.downcast_mut::<T>()) {
|
||||
Some(inner) => {
|
||||
*inner = value;
|
||||
None
|
||||
}
|
||||
None => Some(value),
|
||||
}
|
||||
})
|
||||
S::try_set(self.node, value)
|
||||
}
|
||||
|
||||
/// Sets the value to a new value.
|
||||
pub fn set_value(&self, value: T) {
|
||||
self.update_value(|n| *n = value);
|
||||
}
|
||||
|
||||
/// Returns `true` if the value has not yet been disposed.
|
||||
pub fn exists(&self) -> bool
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
Arena::with(|arena| arena.contains_key(self.node))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IsDisposed for StoredValue<T> {
|
||||
impl<T, S> IsDisposed for StoredValue<T, S> {
|
||||
fn is_disposed(&self) -> bool {
|
||||
Arena::with(|arena| arena.contains_key(self.node))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> StoredValue<T>
|
||||
impl<T, S: Storage<T>> StoredValue<T, S>
|
||||
where
|
||||
T: Clone + 'static,
|
||||
{
|
||||
|
@ -194,13 +316,9 @@ where
|
|||
pub fn get_value(&self) -> T {
|
||||
self.with_value(T::clone)
|
||||
}
|
||||
|
||||
pub(crate) fn get(&self) -> Option<T> {
|
||||
self.try_get_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Dispose for StoredValue<T> {
|
||||
impl<T, S> Dispose for StoredValue<T, S> {
|
||||
fn dispose(self) {
|
||||
Arena::with_mut(|arena| arena.remove(self.node));
|
||||
}
|
||||
|
@ -210,8 +328,9 @@ impl<T> Dispose for StoredValue<T> {
|
|||
#[inline(always)]
|
||||
#[track_caller]
|
||||
#[deprecated = "This function is being removed to conform to Rust idioms. \
|
||||
Please use `StoredValue::new()` instead."]
|
||||
pub fn store_value<T>(value: T) -> StoredValue<T>
|
||||
Please use `StoredValue::new()` or `StoredValue::new_local()` \
|
||||
instead."]
|
||||
pub fn store_value<T>(value: T) -> StoredValue<T, SyncStorage>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
{
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
use crate::{
|
||||
computed::{ArcMemo, Memo},
|
||||
owner::Storage,
|
||||
signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},
|
||||
traits::With,
|
||||
wrappers::read::{MaybeProp, MaybeSignal, Signal},
|
||||
wrappers::read::{MaybeProp, MaybeSignal, Signal, SignalTypes},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
impl<T: Send + Sync + Serialize + 'static> Serialize for ReadSignal<T> {
|
||||
impl<T, St> Serialize for ReadSignal<T, St>
|
||||
where
|
||||
T: Serialize + 'static,
|
||||
St: Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
|
@ -15,7 +20,11 @@ impl<T: Send + Sync + Serialize + 'static> Serialize for ReadSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Serialize + 'static> Serialize for RwSignal<T> {
|
||||
impl<T, St> Serialize for RwSignal<T, St>
|
||||
where
|
||||
T: Serialize + 'static,
|
||||
St: Storage<ArcRwSignal<T>>,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
|
@ -24,7 +33,11 @@ impl<T: Send + Sync + Serialize + 'static> Serialize for RwSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Serialize + 'static> Serialize for Memo<T> {
|
||||
impl<T, St> Serialize for Memo<T, St>
|
||||
where
|
||||
T: Send + Sync + Serialize + 'static,
|
||||
St: Storage<ArcMemo<T>>,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
|
@ -60,7 +73,11 @@ impl<T: Send + Sync + Serialize + 'static> Serialize for ArcMemo<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Serialize> Serialize for MaybeSignal<T> {
|
||||
impl<T, St> Serialize for MaybeSignal<T, St>
|
||||
where
|
||||
T: Send + Sync + Serialize,
|
||||
St: Storage<SignalTypes<T>>,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
|
@ -69,7 +86,11 @@ impl<T: Send + Sync + Serialize> Serialize for MaybeSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Serialize> Serialize for MaybeProp<T> {
|
||||
impl<T, St> Serialize for MaybeProp<T, St>
|
||||
where
|
||||
T: Send + Sync + Serialize,
|
||||
St: Storage<SignalTypes<Option<T>>>,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
|
@ -88,7 +109,11 @@ impl<T: Send + Sync + Serialize> Serialize for MaybeProp<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Clone + Serialize> Serialize for Signal<T> {
|
||||
impl<T, St> Serialize for Signal<T, St>
|
||||
where
|
||||
T: Send + Sync + Serialize + 'static,
|
||||
St: Storage<SignalTypes<T>>,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
|
@ -99,14 +124,16 @@ impl<T: Send + Sync + Clone + Serialize> Serialize for Signal<T> {
|
|||
|
||||
/* Deserialization for signal types */
|
||||
|
||||
impl<'de, T: Send + Sync + Deserialize<'de> + 'static> Deserialize<'de>
|
||||
for RwSignal<T>
|
||||
impl<'de, T, S> Deserialize<'de> for RwSignal<T, S>
|
||||
where
|
||||
T: Send + Sync + Deserialize<'de> + 'static,
|
||||
S: Storage<ArcRwSignal<T>>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
T::deserialize(deserializer).map(RwSignal::new)
|
||||
T::deserialize(deserializer).map(RwSignal::new_with_storage)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,7 +146,7 @@ impl<'de, T: Deserialize<'de>> Deserialize<'de> for ArcRwSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'de, T: Deserialize<'de>> Deserialize<'de> for MaybeSignal<T> {
|
||||
impl<'de, T: Deserialize<'de>, S> Deserialize<'de> for MaybeSignal<T, S> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
|
|
|
@ -11,6 +11,7 @@ mod rw;
|
|||
mod subscriber_traits;
|
||||
mod write;
|
||||
|
||||
use crate::owner::{LocalStorage, SyncStorage};
|
||||
pub use arc_read::*;
|
||||
pub use arc_rw::*;
|
||||
pub use arc_trigger::*;
|
||||
|
@ -107,10 +108,24 @@ pub fn arc_signal<T>(value: T) -> (ArcReadSignal<T>, ArcWriteSignal<T>) {
|
|||
/// ```
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
pub fn signal<T: Send + Sync>(value: T) -> (ReadSignal<T>, WriteSignal<T>) {
|
||||
pub fn signal<T: Send + Sync + 'static>(
|
||||
value: T,
|
||||
) -> (ReadSignal<T, SyncStorage>, WriteSignal<T, SyncStorage>) {
|
||||
RwSignal::new(value).split()
|
||||
}
|
||||
|
||||
/// Creates an arena-allocated signal.
|
||||
///
|
||||
/// Unlike [`signal`], this does not require the value to be `Send + Sync`. Instead, it is stored
|
||||
/// on a local arena. Accessing either of the returned signals from another thread will panic.
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
pub fn signal_local<T: 'static>(
|
||||
value: T,
|
||||
) -> (ReadSignal<T, LocalStorage>, WriteSignal<T, LocalStorage>) {
|
||||
RwSignal::new_local(value).split()
|
||||
}
|
||||
|
||||
/// Creates an arena-allocated signal, the basic reactive primitive.
|
||||
///
|
||||
/// A signal is a piece of data that may change over time, and notifies other
|
||||
|
@ -155,8 +170,8 @@ pub fn signal<T: Send + Sync>(value: T) -> (ReadSignal<T>, WriteSignal<T>) {
|
|||
#[track_caller]
|
||||
#[deprecated = "This function is being renamed to `signal()` to conform to \
|
||||
Rust idioms."]
|
||||
pub fn create_signal<T: Send + Sync>(
|
||||
pub fn create_signal<T: Send + Sync + 'static>(
|
||||
value: T,
|
||||
) -> (ReadSignal<T>, WriteSignal<T>) {
|
||||
) -> (ReadSignal<T, SyncStorage>, WriteSignal<T, SyncStorage>) {
|
||||
signal(value)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use super::{
|
|||
};
|
||||
use crate::{
|
||||
graph::SubscriberSet,
|
||||
owner::StoredValue,
|
||||
owner::{Storage, StoredValue, SyncStorage},
|
||||
traits::{DefinedAt, Dispose, IsDisposed, ReadUntracked},
|
||||
unwrap_signal,
|
||||
};
|
||||
|
@ -57,27 +57,30 @@ use std::{
|
|||
/// // calling .read() accesses the value by reference
|
||||
/// assert_eq!(count.read(), 0);
|
||||
/// ```
|
||||
pub struct ReadSignal<T: 'static> {
|
||||
pub struct ReadSignal<T, S = SyncStorage> {
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) defined_at: &'static Location<'static>,
|
||||
pub(crate) inner: StoredValue<ArcReadSignal<T>>,
|
||||
pub(crate) inner: StoredValue<ArcReadSignal<T>, S>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Dispose for ReadSignal<T> {
|
||||
impl<T, S> Dispose for ReadSignal<T, S> {
|
||||
fn dispose(self) {
|
||||
self.inner.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for ReadSignal<T> {}
|
||||
impl<T, S> Copy for ReadSignal<T, S> {}
|
||||
|
||||
impl<T> Clone for ReadSignal<T> {
|
||||
impl<T, S> Clone for ReadSignal<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for ReadSignal<T> {
|
||||
impl<T, S> Debug for ReadSignal<T, S>
|
||||
where
|
||||
S: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ReadSignal")
|
||||
.field("type", &std::any::type_name::<T>())
|
||||
|
@ -86,21 +89,21 @@ impl<T> Debug for ReadSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for ReadSignal<T> {
|
||||
impl<T, S> PartialEq for ReadSignal<T, S> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for ReadSignal<T> {}
|
||||
impl<T, S> Eq for ReadSignal<T, S> {}
|
||||
|
||||
impl<T> Hash for ReadSignal<T> {
|
||||
impl<T, S> Hash for ReadSignal<T, S> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.inner.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DefinedAt for ReadSignal<T> {
|
||||
impl<T, S> DefinedAt for ReadSignal<T, S> {
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -113,13 +116,16 @@ impl<T> DefinedAt for ReadSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> IsDisposed for ReadSignal<T> {
|
||||
impl<T, S> IsDisposed for ReadSignal<T, S> {
|
||||
fn is_disposed(&self) -> bool {
|
||||
!self.inner.exists()
|
||||
self.inner.is_disposed()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsSubscriberSet for ReadSignal<T> {
|
||||
impl<T, S> AsSubscriberSet for ReadSignal<T, S>
|
||||
where
|
||||
S: Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
type Output = Arc<RwLock<SubscriberSet>>;
|
||||
|
||||
fn as_subscriber_set(&self) -> Option<Self::Output> {
|
||||
|
@ -129,7 +135,11 @@ impl<T> AsSubscriberSet for ReadSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> ReadUntracked for ReadSignal<T> {
|
||||
impl<T, S> ReadUntracked for ReadSignal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
type Value = ReadGuard<T, Plain<T>>;
|
||||
|
||||
fn try_read_untracked(&self) -> Option<Self::Value> {
|
||||
|
@ -139,20 +149,31 @@ impl<T: 'static> ReadUntracked for ReadSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> From<ArcReadSignal<T>> for ReadSignal<T> {
|
||||
impl<T, S> From<ArcReadSignal<T>> for ReadSignal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: ArcReadSignal<T>) -> Self {
|
||||
ReadSignal {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(value),
|
||||
inner: StoredValue::new_with_storage(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> From<ReadSignal<T>> for ArcReadSignal<T> {
|
||||
impl<T, S> From<ReadSignal<T, S>> for ArcReadSignal<T>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: ReadSignal<T>) -> Self {
|
||||
value.inner.get().unwrap_or_else(unwrap_signal!(value))
|
||||
fn from(value: ReadSignal<T, S>) -> Self {
|
||||
value
|
||||
.inner
|
||||
.try_get_value()
|
||||
.unwrap_or_else(unwrap_signal!(value))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use super::{
|
||||
guards::{Plain, ReadGuard},
|
||||
subscriber_traits::AsSubscriberSet,
|
||||
ArcRwSignal, ReadSignal, WriteSignal,
|
||||
ArcReadSignal, ArcRwSignal, ArcWriteSignal, ReadSignal, WriteSignal,
|
||||
};
|
||||
use crate::{
|
||||
graph::{ReactiveNode, SubscriberSet},
|
||||
owner::StoredValue,
|
||||
owner::{LocalStorage, Storage, StoredValue, SyncStorage},
|
||||
signal::guards::{UntrackedWriteGuard, WriteGuard},
|
||||
traits::{
|
||||
DefinedAt, Dispose, IsDisposed, ReadUntracked, Trigger,
|
||||
|
@ -99,19 +99,22 @@ use std::{
|
|||
/// count.set(1);
|
||||
/// assert_eq!(double_count(), 2);
|
||||
/// ```
|
||||
pub struct RwSignal<T> {
|
||||
pub struct RwSignal<T, S = SyncStorage> {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
inner: StoredValue<ArcRwSignal<T>>,
|
||||
inner: StoredValue<ArcRwSignal<T>, S>,
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> Dispose for RwSignal<T> {
|
||||
impl<T, S> Dispose for RwSignal<T, S> {
|
||||
fn dispose(self) {
|
||||
self.inner.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> RwSignal<T> {
|
||||
impl<T> RwSignal<T, SyncStorage>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
{
|
||||
/// Creates a new signal, taking the initial value as its argument.
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
|
@ -119,63 +122,118 @@ impl<T: Send + Sync + 'static> RwSignal<T> {
|
|||
)]
|
||||
#[track_caller]
|
||||
pub fn new(value: T) -> Self {
|
||||
Self::new_with_storage(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> RwSignal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcRwSignal<T>>,
|
||||
{
|
||||
/// Creates a new signal with the given arena storage method.
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
#[track_caller]
|
||||
pub fn new_with_storage(value: T) -> Self {
|
||||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(ArcRwSignal::new(value)),
|
||||
inner: StoredValue::new_with_storage(ArcRwSignal::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RwSignal<T, LocalStorage>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
/// Creates a new signal, taking the initial value as its argument. Unlike [`RwSignal::new`],
|
||||
/// this pins the value to the current thread. Accessing it from any other thread will panic.
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(level = "trace", skip_all,)
|
||||
)]
|
||||
#[track_caller]
|
||||
pub fn new_local(value: T) -> Self {
|
||||
Self::new_with_storage(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> RwSignal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcRwSignal<T>> + Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
/// Returns a read-only handle to the signal.
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
pub fn read_only(&self) -> ReadSignal<T> {
|
||||
pub fn read_only(&self) -> ReadSignal<T, S> {
|
||||
ReadSignal {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(
|
||||
inner: StoredValue::new_with_storage(
|
||||
self.inner
|
||||
.get()
|
||||
.try_get_value()
|
||||
.map(|inner| inner.read_only())
|
||||
.unwrap_or_else(unwrap_signal!(self)),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> RwSignal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcRwSignal<T>> + Storage<ArcWriteSignal<T>>,
|
||||
{
|
||||
/// Returns a write-only handle to the signal.
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
pub fn write_only(&self) -> WriteSignal<T> {
|
||||
pub fn write_only(&self) -> WriteSignal<T, S> {
|
||||
WriteSignal {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(
|
||||
inner: StoredValue::new_with_storage(
|
||||
self.inner
|
||||
.get()
|
||||
.try_get_value()
|
||||
.map(|inner| inner.write_only())
|
||||
.unwrap_or_else(unwrap_signal!(self)),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> RwSignal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcRwSignal<T>>
|
||||
+ Storage<ArcWriteSignal<T>>
|
||||
+ Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
/// Splits the signal into its readable and writable halves.
|
||||
#[track_caller]
|
||||
#[inline(always)]
|
||||
pub fn split(&self) -> (ReadSignal<T>, WriteSignal<T>) {
|
||||
pub fn split(&self) -> (ReadSignal<T, S>, WriteSignal<T, S>) {
|
||||
(self.read_only(), self.write_only())
|
||||
}
|
||||
|
||||
/// Reunites the two halves of a signal. Returns `None` if the two signals
|
||||
/// provided were not created from the same signal.
|
||||
#[track_caller]
|
||||
pub fn unite(read: ReadSignal<T>, write: WriteSignal<T>) -> Option<Self> {
|
||||
match (read.inner.get(), write.inner.get()) {
|
||||
pub fn unite(
|
||||
read: ReadSignal<T, S>,
|
||||
write: WriteSignal<T, S>,
|
||||
) -> Option<Self> {
|
||||
match (read.inner.try_get_value(), write.inner.try_get_value()) {
|
||||
(Some(read), Some(write)) => {
|
||||
if Arc::ptr_eq(&read.inner, &write.inner) {
|
||||
Some(Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(ArcRwSignal {
|
||||
inner: StoredValue::new_with_storage(ArcRwSignal {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
value: Arc::clone(&read.value),
|
||||
|
@ -191,15 +249,18 @@ impl<T: Send + Sync + 'static> RwSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for RwSignal<T> {}
|
||||
impl<T, S> Copy for RwSignal<T, S> {}
|
||||
|
||||
impl<T> Clone for RwSignal<T> {
|
||||
impl<T, S> Clone for RwSignal<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for RwSignal<T> {
|
||||
impl<T, S> Debug for RwSignal<T, S>
|
||||
where
|
||||
S: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RwSignal")
|
||||
.field("type", &std::any::type_name::<T>())
|
||||
|
@ -208,28 +269,32 @@ impl<T> Debug for RwSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static + Default> Default for RwSignal<T> {
|
||||
impl<T, S> Default for RwSignal<T, S>
|
||||
where
|
||||
T: Default + 'static,
|
||||
S: Storage<ArcRwSignal<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn default() -> Self {
|
||||
Self::new(T::default())
|
||||
Self::new_with_storage(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for RwSignal<T> {
|
||||
impl<T, S> PartialEq for RwSignal<T, S> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for RwSignal<T> {}
|
||||
impl<T, S> Eq for RwSignal<T, S> {}
|
||||
|
||||
impl<T> Hash for RwSignal<T> {
|
||||
impl<T, S> Hash for RwSignal<T, S> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.inner.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DefinedAt for RwSignal<T> {
|
||||
impl<T, S> DefinedAt for RwSignal<T, S> {
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -242,13 +307,16 @@ impl<T> DefinedAt for RwSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> IsDisposed for RwSignal<T> {
|
||||
impl<T: 'static, S> IsDisposed for RwSignal<T, S> {
|
||||
fn is_disposed(&self) -> bool {
|
||||
!self.inner.exists()
|
||||
self.inner.is_disposed()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> AsSubscriberSet for RwSignal<T> {
|
||||
impl<T, S> AsSubscriberSet for RwSignal<T, S>
|
||||
where
|
||||
S: Storage<ArcRwSignal<T>>,
|
||||
{
|
||||
type Output = Arc<RwLock<SubscriberSet>>;
|
||||
|
||||
fn as_subscriber_set(&self) -> Option<Self::Output> {
|
||||
|
@ -258,21 +326,34 @@ impl<T: 'static> AsSubscriberSet for RwSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> ReadUntracked for RwSignal<T> {
|
||||
impl<T, S> ReadUntracked for RwSignal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcRwSignal<T>>,
|
||||
{
|
||||
type Value = ReadGuard<T, Plain<T>>;
|
||||
|
||||
fn try_read_untracked(&self) -> Option<Self::Value> {
|
||||
self.inner.get().map(|inner| inner.read_untracked())
|
||||
self.inner
|
||||
.try_get_value()
|
||||
.map(|inner| inner.read_untracked())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Trigger for RwSignal<T> {
|
||||
impl<T, S> Trigger for RwSignal<T, S>
|
||||
where
|
||||
S: Storage<ArcRwSignal<T>>,
|
||||
{
|
||||
fn trigger(&self) {
|
||||
self.mark_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Writeable for RwSignal<T> {
|
||||
impl<T, S> Writeable for RwSignal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcRwSignal<T>>,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
|
||||
|
@ -288,27 +369,42 @@ impl<T: 'static> Writeable for RwSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> From<ArcRwSignal<T>> for RwSignal<T> {
|
||||
impl<T, S> From<ArcRwSignal<T>> for RwSignal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcRwSignal<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: ArcRwSignal<T>) -> Self {
|
||||
RwSignal {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(value),
|
||||
inner: StoredValue::new_with_storage(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Send + Sync + 'static> From<&'a ArcRwSignal<T>> for RwSignal<T> {
|
||||
impl<'a, T, S> From<&'a ArcRwSignal<T>> for RwSignal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcRwSignal<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: &'a ArcRwSignal<T>) -> Self {
|
||||
value.clone().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> From<RwSignal<T>> for ArcRwSignal<T> {
|
||||
impl<T, S> From<RwSignal<T, S>> for ArcRwSignal<T>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcRwSignal<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: RwSignal<T>) -> Self {
|
||||
value.inner.get().unwrap_or_else(unwrap_signal!(value))
|
||||
fn from(value: RwSignal<T, S>) -> Self {
|
||||
value
|
||||
.inner
|
||||
.try_get_value()
|
||||
.unwrap_or_else(unwrap_signal!(value))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::{guards::WriteGuard, ArcWriteSignal};
|
||||
use crate::{
|
||||
owner::StoredValue,
|
||||
owner::{Storage, StoredValue, SyncStorage},
|
||||
traits::{
|
||||
DefinedAt, Dispose, IsDisposed, Trigger, UntrackableGuard, Writeable,
|
||||
},
|
||||
|
@ -51,27 +51,30 @@ use std::{hash::Hash, ops::DerefMut, panic::Location, sync::Arc};
|
|||
/// *set_count.write() += 1;
|
||||
/// assert_eq!(count.get(), 3);
|
||||
/// ```
|
||||
pub struct WriteSignal<T> {
|
||||
pub struct WriteSignal<T, S = SyncStorage> {
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) defined_at: &'static Location<'static>,
|
||||
pub(crate) inner: StoredValue<ArcWriteSignal<T>>,
|
||||
pub(crate) inner: StoredValue<ArcWriteSignal<T>, S>,
|
||||
}
|
||||
|
||||
impl<T> Dispose for WriteSignal<T> {
|
||||
impl<T, S> Dispose for WriteSignal<T, S> {
|
||||
fn dispose(self) {
|
||||
self.inner.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for WriteSignal<T> {}
|
||||
impl<T, S> Copy for WriteSignal<T, S> {}
|
||||
|
||||
impl<T> Clone for WriteSignal<T> {
|
||||
impl<T, S> Clone for WriteSignal<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for WriteSignal<T> {
|
||||
impl<T, S> Debug for WriteSignal<T, S>
|
||||
where
|
||||
S: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("WriteSignal")
|
||||
.field("type", &std::any::type_name::<T>())
|
||||
|
@ -80,21 +83,21 @@ impl<T> Debug for WriteSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for WriteSignal<T> {
|
||||
impl<T, S> PartialEq for WriteSignal<T, S> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for WriteSignal<T> {}
|
||||
impl<T, S> Eq for WriteSignal<T, S> {}
|
||||
|
||||
impl<T> Hash for WriteSignal<T> {
|
||||
impl<T, S> Hash for WriteSignal<T, S> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.inner.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DefinedAt for WriteSignal<T> {
|
||||
impl<T, S> DefinedAt for WriteSignal<T, S> {
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -107,21 +110,29 @@ impl<T> DefinedAt for WriteSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> IsDisposed for WriteSignal<T> {
|
||||
impl<T, S> IsDisposed for WriteSignal<T, S> {
|
||||
fn is_disposed(&self) -> bool {
|
||||
!self.inner.exists()
|
||||
self.inner.is_disposed()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Trigger for WriteSignal<T> {
|
||||
impl<T, S> Trigger for WriteSignal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcWriteSignal<T>>,
|
||||
{
|
||||
fn trigger(&self) {
|
||||
if let Some(inner) = self.inner.get() {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Writeable for WriteSignal<T> {
|
||||
impl<T, S> Writeable for WriteSignal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcWriteSignal<T>>,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
|
||||
|
|
|
@ -4,16 +4,20 @@
|
|||
pub mod read {
|
||||
use crate::{
|
||||
computed::{ArcMemo, Memo},
|
||||
owner::StoredValue,
|
||||
owner::{Storage, StoredValue, SyncStorage},
|
||||
signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},
|
||||
traits::{DefinedAt, Dispose, Get, With, WithUntracked},
|
||||
untrack, unwrap_signal,
|
||||
};
|
||||
use std::{panic::Location, sync::Arc};
|
||||
|
||||
enum SignalTypes<T: 'static> {
|
||||
/// Possibilities for the inner type of a [`Signal`].
|
||||
pub enum SignalTypes<T> {
|
||||
/// A readable signal.
|
||||
ReadSignal(ArcReadSignal<T>),
|
||||
/// A memo.
|
||||
Memo(ArcMemo<T>),
|
||||
/// A derived signal.
|
||||
DerivedSignal(Arc<dyn Fn() -> T + Send + Sync>),
|
||||
}
|
||||
|
||||
|
@ -237,27 +241,30 @@ pub mod read {
|
|||
/// This allows you to create APIs that take any kind of `Signal<T>` as an argument,
|
||||
/// rather than adding a generic `F: Fn() -> T`. Values can be access with the same
|
||||
/// function call, `with()`, and `get()` APIs as other signals.
|
||||
pub struct Signal<T: 'static> {
|
||||
pub struct Signal<T, S = SyncStorage> {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
inner: StoredValue<SignalTypes<T>>,
|
||||
inner: StoredValue<SignalTypes<T>, S>,
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> Dispose for Signal<T> {
|
||||
impl<T, S> Dispose for Signal<T, S> {
|
||||
fn dispose(self) {
|
||||
self.inner.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Signal<T> {
|
||||
impl<T, S> Clone for Signal<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for Signal<T> {}
|
||||
impl<T, S> Copy for Signal<T, S> {}
|
||||
|
||||
impl<T> core::fmt::Debug for Signal<T> {
|
||||
impl<T, S> core::fmt::Debug for Signal<T, S>
|
||||
where
|
||||
S: std::fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let mut s = f.debug_struct("Signal");
|
||||
s.field("inner", &self.inner);
|
||||
|
@ -267,15 +274,15 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Signal<T> {}
|
||||
impl<T, S> Eq for Signal<T, S> {}
|
||||
|
||||
impl<T> PartialEq for Signal<T> {
|
||||
impl<T, S> PartialEq for Signal<T, S> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DefinedAt for Signal<T> {
|
||||
impl<T, S> DefinedAt for Signal<T, S> {
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -288,9 +295,10 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> WithUntracked for Signal<T>
|
||||
impl<T, S> WithUntracked for Signal<T, S>
|
||||
where
|
||||
T: Send + Sync,
|
||||
T: 'static,
|
||||
S: Storage<SignalTypes<T>>,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
|
@ -312,9 +320,10 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> With for Signal<T>
|
||||
impl<T, S> With for Signal<T, S>
|
||||
where
|
||||
T: Send + Sync,
|
||||
T: Send + Sync + 'static,
|
||||
S: Storage<SignalTypes<T>>,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
|
@ -334,9 +343,10 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Signal<T>
|
||||
impl<T, S> Signal<T, S>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
T: 'static,
|
||||
S: Storage<SignalTypes<T>>,
|
||||
{
|
||||
/// Wraps a derived signal, i.e., any computation that accesses one or more
|
||||
/// reactive signals.
|
||||
|
@ -369,18 +379,19 @@ pub mod read {
|
|||
};
|
||||
|
||||
Self {
|
||||
inner: StoredValue::new(SignalTypes::DerivedSignal(Arc::new(
|
||||
derived_signal,
|
||||
))),
|
||||
inner: StoredValue::new_with_storage(
|
||||
SignalTypes::DerivedSignal(Arc::new(derived_signal)),
|
||||
),
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: std::panic::Location::caller(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Signal<T>
|
||||
impl<T, S> Default for Signal<T, S>
|
||||
where
|
||||
T: Default + Send + Sync + 'static,
|
||||
T: Default + 'static,
|
||||
S: Storage<SignalTypes<T>>,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::derive(|| Default::default())
|
||||
|
@ -394,51 +405,77 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Send + Sync + 'static> From<T> for Signal<T> {
|
||||
impl<T, S> From<T> for Signal<T, S>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
S: Storage<SignalTypes<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: T) -> Self {
|
||||
Self::derive(move || value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> From<ArcSignal<T>> for Signal<T> {
|
||||
impl<T, S> From<ArcSignal<T>> for Signal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<SignalTypes<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: ArcSignal<T>) -> Self {
|
||||
Signal {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(value.inner),
|
||||
inner: StoredValue::new_with_storage(value.inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> From<Signal<T>> for ArcSignal<T> {
|
||||
impl<T, S> From<Signal<T, S>> for ArcSignal<T>
|
||||
where
|
||||
S: Storage<SignalTypes<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: Signal<T>) -> Self {
|
||||
fn from(value: Signal<T, S>) -> Self {
|
||||
ArcSignal {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: value.inner.get().unwrap_or_else(unwrap_signal!(value)),
|
||||
inner: value
|
||||
.inner
|
||||
.try_get_value()
|
||||
.unwrap_or_else(unwrap_signal!(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<ReadSignal<T>> for Signal<T> {
|
||||
impl<T, S> From<ReadSignal<T, S>> for Signal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<SignalTypes<T>> + Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: ReadSignal<T>) -> Self {
|
||||
fn from(value: ReadSignal<T, S>) -> Self {
|
||||
Self {
|
||||
inner: StoredValue::new(SignalTypes::ReadSignal(value.into())),
|
||||
inner: StoredValue::new_with_storage(SignalTypes::ReadSignal(
|
||||
value.into(),
|
||||
)),
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: std::panic::Location::caller(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<RwSignal<T>> for Signal<T> {
|
||||
impl<T, S> From<RwSignal<T, S>> for Signal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<SignalTypes<T>>
|
||||
+ Storage<ArcRwSignal<T>>
|
||||
+ Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: RwSignal<T>) -> Self {
|
||||
fn from(value: RwSignal<T, S>) -> Self {
|
||||
Self {
|
||||
inner: StoredValue::new(SignalTypes::ReadSignal(
|
||||
inner: StoredValue::new_with_storage(SignalTypes::ReadSignal(
|
||||
value.read_only().into(),
|
||||
)),
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -447,11 +484,17 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<Memo<T>> for Signal<T> {
|
||||
impl<T, S> From<Memo<T, S>> for Signal<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<SignalTypes<T>> + Storage<ArcMemo<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: Memo<T>) -> Self {
|
||||
fn from(value: Memo<T, S>) -> Self {
|
||||
Self {
|
||||
inner: StoredValue::new(SignalTypes::Memo(value.into())),
|
||||
inner: StoredValue::new_with_storage(SignalTypes::Memo(
|
||||
value.into(),
|
||||
)),
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: std::panic::Location::caller(),
|
||||
}
|
||||
|
@ -486,17 +529,17 @@ pub mod read {
|
|||
/// assert_eq!(above_3(&memoized_double_count.into()), true);
|
||||
/// ```
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum MaybeSignal<T>
|
||||
pub enum MaybeSignal<T, S = SyncStorage>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
/// An unchanging value of type `T`.
|
||||
Static(T),
|
||||
/// A reactive signal that contains a value of type `T`.
|
||||
Dynamic(Signal<T>),
|
||||
Dynamic(Signal<T, S>),
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for MaybeSignal<T> {
|
||||
impl<T: Clone, S> Clone for MaybeSignal<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Static(item) => Self::Static(item.clone()),
|
||||
|
@ -505,15 +548,15 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> Copy for MaybeSignal<T> {}
|
||||
impl<T: Copy, S> Copy for MaybeSignal<T, S> {}
|
||||
|
||||
impl<T: Default> Default for MaybeSignal<T> {
|
||||
impl<T: Default, S> Default for MaybeSignal<T, S> {
|
||||
fn default() -> Self {
|
||||
Self::Static(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DefinedAt for MaybeSignal<T> {
|
||||
impl<T, S> DefinedAt for MaybeSignal<T, S> {
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
// TODO this could be improved, but would require moving from an enum to a struct here.
|
||||
// Probably not worth it for relatively small benefits.
|
||||
|
@ -521,7 +564,10 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> WithUntracked for MaybeSignal<T> {
|
||||
impl<T, S> WithUntracked for MaybeSignal<T, S>
|
||||
where
|
||||
S: Storage<SignalTypes<T>>,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn try_with_untracked<U>(
|
||||
|
@ -535,7 +581,11 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> With for MaybeSignal<T> {
|
||||
impl<T, S> With for MaybeSignal<T, S>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
S: Storage<SignalTypes<T>>,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn try_with<U>(
|
||||
|
@ -549,9 +599,9 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> MaybeSignal<T>
|
||||
impl<T, S> MaybeSignal<T, S>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
S: Storage<SignalTypes<T>>,
|
||||
{
|
||||
/// Wraps a derived signal, i.e., any computation that accesses one or more
|
||||
/// reactive signals.
|
||||
|
@ -562,55 +612,77 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for MaybeSignal<T> {
|
||||
impl<T, S> From<T> for MaybeSignal<T, S> {
|
||||
fn from(value: T) -> Self {
|
||||
Self::Static(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<ReadSignal<T>> for MaybeSignal<T> {
|
||||
fn from(value: ReadSignal<T>) -> Self {
|
||||
impl<T, S> From<ReadSignal<T, S>> for MaybeSignal<T, S>
|
||||
where
|
||||
S: Storage<SignalTypes<T>> + Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
fn from(value: ReadSignal<T, S>) -> Self {
|
||||
Self::Dynamic(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<RwSignal<T>> for MaybeSignal<T> {
|
||||
fn from(value: RwSignal<T>) -> Self {
|
||||
impl<T, S> From<RwSignal<T, S>> for MaybeSignal<T, S>
|
||||
where
|
||||
S: Storage<SignalTypes<T>>
|
||||
+ Storage<ArcRwSignal<T>>
|
||||
+ Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
fn from(value: RwSignal<T, S>) -> Self {
|
||||
Self::Dynamic(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<Memo<T>> for MaybeSignal<T> {
|
||||
fn from(value: Memo<T>) -> Self {
|
||||
impl<T, S> From<Memo<T, S>> for MaybeSignal<T, S>
|
||||
where
|
||||
S: Storage<SignalTypes<T>> + Storage<ArcMemo<T>>,
|
||||
{
|
||||
fn from(value: Memo<T, S>) -> Self {
|
||||
Self::Dynamic(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<ArcReadSignal<T>> for MaybeSignal<T> {
|
||||
impl<T, S> From<ArcReadSignal<T>> for MaybeSignal<T, S>
|
||||
where
|
||||
S: Storage<SignalTypes<T>> + Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
fn from(value: ArcReadSignal<T>) -> Self {
|
||||
ReadSignal::from(value).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<ArcRwSignal<T>> for MaybeSignal<T> {
|
||||
impl<T, S> From<ArcRwSignal<T>> for MaybeSignal<T, S>
|
||||
where
|
||||
S: Storage<SignalTypes<T>>
|
||||
+ Storage<ArcRwSignal<T>>
|
||||
+ Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
fn from(value: ArcRwSignal<T>) -> Self {
|
||||
RwSignal::from(value).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<ArcMemo<T>> for MaybeSignal<T> {
|
||||
impl<T, S> From<ArcMemo<T>> for MaybeSignal<T, S>
|
||||
where
|
||||
S: Storage<SignalTypes<T>> + Storage<ArcMemo<T>>,
|
||||
{
|
||||
fn from(value: ArcMemo<T>) -> Self {
|
||||
Memo::from(value).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Signal<T>> for MaybeSignal<T> {
|
||||
fn from(value: Signal<T>) -> Self {
|
||||
impl<T, S> From<Signal<T, S>> for MaybeSignal<T, S> {
|
||||
fn from(value: Signal<T, S>) -> Self {
|
||||
Self::Dynamic(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for MaybeSignal<String> {
|
||||
impl<S> From<&str> for MaybeSignal<String, S> {
|
||||
fn from(value: &str) -> Self {
|
||||
Self::Static(value.to_string())
|
||||
}
|
||||
|
@ -647,27 +719,36 @@ pub mod read {
|
|||
/// assert_eq!(above_3(&double_count), true);
|
||||
/// assert_eq!(above_3(&memoized_double_count.into()), true);
|
||||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct MaybeProp<T: Send + Sync + 'static>(
|
||||
pub(crate) Option<MaybeSignal<Option<T>>>,
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct MaybeProp<T: 'static, S = SyncStorage>(
|
||||
pub(crate) Option<MaybeSignal<Option<T>, S>>,
|
||||
);
|
||||
|
||||
impl<T: Send + Sync + Copy> Copy for MaybeProp<T> {}
|
||||
impl<T: Clone, S> Clone for MaybeProp<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> Default for MaybeProp<T> {
|
||||
impl<T: Copy, S> Copy for MaybeProp<T, S> {}
|
||||
|
||||
impl<T, S> Default for MaybeProp<T, S> {
|
||||
fn default() -> Self {
|
||||
Self(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> DefinedAt for MaybeProp<T> {
|
||||
impl<T, S> DefinedAt for MaybeProp<T, S> {
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
// TODO this can be improved by adding a defined_at field
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> WithUntracked for MaybeProp<T> {
|
||||
impl<T, S> WithUntracked for MaybeProp<T, S>
|
||||
where
|
||||
S: Storage<SignalTypes<Option<T>>>,
|
||||
{
|
||||
type Value = Option<T>;
|
||||
|
||||
fn try_with_untracked<U>(
|
||||
|
@ -681,7 +762,11 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> With for MaybeProp<T> {
|
||||
impl<T, S> With for MaybeProp<T, S>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
S: Storage<SignalTypes<Option<T>>>,
|
||||
{
|
||||
type Value = Option<T>;
|
||||
|
||||
fn try_with<U>(
|
||||
|
@ -695,9 +780,9 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> MaybeProp<T>
|
||||
impl<T, S> MaybeProp<T, S>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
S: Storage<SignalTypes<Option<T>>>,
|
||||
{
|
||||
/// Wraps a derived signal, i.e., any computation that accesses one or more
|
||||
/// reactive signals.
|
||||
|
@ -708,79 +793,106 @@ pub mod read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<T> for MaybeProp<T> {
|
||||
impl<T, S> From<T> for MaybeProp<T, S> {
|
||||
fn from(value: T) -> Self {
|
||||
Self(Some(MaybeSignal::from(Some(value))))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<Option<T>> for MaybeProp<T> {
|
||||
impl<T, S> From<Option<T>> for MaybeProp<T, S> {
|
||||
fn from(value: Option<T>) -> Self {
|
||||
Self(Some(MaybeSignal::from(value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<MaybeSignal<Option<T>>> for MaybeProp<T> {
|
||||
fn from(value: MaybeSignal<Option<T>>) -> Self {
|
||||
impl<T, S> From<MaybeSignal<Option<T>, S>> for MaybeProp<T, S> {
|
||||
fn from(value: MaybeSignal<Option<T>, S>) -> Self {
|
||||
Self(Some(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<Option<MaybeSignal<Option<T>>>> for MaybeProp<T> {
|
||||
fn from(value: Option<MaybeSignal<Option<T>>>) -> Self {
|
||||
impl<T, S> From<Option<MaybeSignal<Option<T>, S>>> for MaybeProp<T, S> {
|
||||
fn from(value: Option<MaybeSignal<Option<T>, S>>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<ReadSignal<Option<T>>> for MaybeProp<T> {
|
||||
fn from(value: ReadSignal<Option<T>>) -> Self {
|
||||
impl<T, S> From<ReadSignal<Option<T>, S>> for MaybeProp<T, S>
|
||||
where
|
||||
S: Storage<SignalTypes<Option<T>>> + Storage<ArcReadSignal<Option<T>>>,
|
||||
{
|
||||
fn from(value: ReadSignal<Option<T>, S>) -> Self {
|
||||
Self(Some(value.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<RwSignal<Option<T>>> for MaybeProp<T> {
|
||||
fn from(value: RwSignal<Option<T>>) -> Self {
|
||||
impl<T, S> From<RwSignal<Option<T>, S>> for MaybeProp<T, S>
|
||||
where
|
||||
S: Storage<SignalTypes<Option<T>>>
|
||||
+ Storage<ArcRwSignal<Option<T>>>
|
||||
+ Storage<ArcReadSignal<Option<T>>>,
|
||||
{
|
||||
fn from(value: RwSignal<Option<T>, S>) -> Self {
|
||||
Self(Some(value.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<Memo<Option<T>>> for MaybeProp<T> {
|
||||
fn from(value: Memo<Option<T>>) -> Self {
|
||||
impl<T, S> From<Memo<Option<T>, S>> for MaybeProp<T, S>
|
||||
where
|
||||
S: Storage<SignalTypes<Option<T>>> + Storage<ArcMemo<Option<T>>>,
|
||||
{
|
||||
fn from(value: Memo<Option<T>, S>) -> Self {
|
||||
Self(Some(value.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<Signal<Option<T>>> for MaybeProp<T> {
|
||||
fn from(value: Signal<Option<T>>) -> Self {
|
||||
impl<T, S> From<Signal<Option<T>, S>> for MaybeProp<T, S> {
|
||||
fn from(value: Signal<Option<T>, S>) -> Self {
|
||||
Self(Some(value.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Clone> From<ReadSignal<T>> for MaybeProp<T> {
|
||||
fn from(value: ReadSignal<T>) -> Self {
|
||||
impl<T, S> From<ReadSignal<T, S>> for MaybeProp<T, S>
|
||||
where
|
||||
T: Send + Sync + Clone,
|
||||
S: Storage<SignalTypes<Option<T>>> + Storage<ArcReadSignal<T>>,
|
||||
{
|
||||
fn from(value: ReadSignal<T, S>) -> Self {
|
||||
Self(Some(MaybeSignal::derive(move || Some(value.get()))))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Clone> From<RwSignal<T>> for MaybeProp<T> {
|
||||
fn from(value: RwSignal<T>) -> Self {
|
||||
impl<T, S> From<RwSignal<T, S>> for MaybeProp<T, S>
|
||||
where
|
||||
T: Send + Sync + Clone,
|
||||
S: Storage<SignalTypes<Option<T>>> + Storage<ArcRwSignal<T>>,
|
||||
{
|
||||
fn from(value: RwSignal<T, S>) -> Self {
|
||||
Self(Some(MaybeSignal::derive(move || Some(value.get()))))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Clone> From<Memo<T>> for MaybeProp<T> {
|
||||
fn from(value: Memo<T>) -> Self {
|
||||
impl<T, S> From<Memo<T, S>> for MaybeProp<T, S>
|
||||
where
|
||||
T: Send + Sync + Clone,
|
||||
S: Storage<SignalTypes<Option<T>>> + Storage<ArcMemo<T>>,
|
||||
{
|
||||
fn from(value: Memo<T, S>) -> Self {
|
||||
Self(Some(MaybeSignal::derive(move || Some(value.get()))))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Clone> From<Signal<T>> for MaybeProp<T> {
|
||||
fn from(value: Signal<T>) -> Self {
|
||||
impl<T, S> From<Signal<T, S>> for MaybeProp<T, S>
|
||||
where
|
||||
T: Send + Sync + Clone,
|
||||
S: Storage<SignalTypes<Option<T>>> + Storage<SignalTypes<T>>,
|
||||
{
|
||||
fn from(value: Signal<T, S>) -> Self {
|
||||
Self(Some(MaybeSignal::derive(move || Some(value.get()))))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for MaybeProp<String> {
|
||||
impl<S> From<&str> for MaybeProp<String, S> {
|
||||
fn from(value: &str) -> Self {
|
||||
Self(Some(MaybeSignal::from(Some(value.to_string()))))
|
||||
}
|
||||
|
@ -790,23 +902,23 @@ pub mod read {
|
|||
/// Types that abstract over the ability to update a signal.
|
||||
pub mod write {
|
||||
use crate::{
|
||||
owner::StoredValue,
|
||||
signal::{RwSignal, WriteSignal},
|
||||
owner::{Storage, StoredValue, SyncStorage},
|
||||
signal::{ArcRwSignal, ArcWriteSignal, RwSignal, WriteSignal},
|
||||
traits::Set,
|
||||
};
|
||||
|
||||
/// Helper trait for converting `Fn(T)` into [`SignalSetter<T>`].
|
||||
pub trait IntoSignalSetter<T>: Sized {
|
||||
pub trait IntoSignalSetter<T, S>: Sized {
|
||||
/// Consumes `self`, returning [`SignalSetter<T>`].
|
||||
fn into_signal_setter(self) -> SignalSetter<T>;
|
||||
fn into_signal_setter(self) -> SignalSetter<T, S>;
|
||||
}
|
||||
|
||||
impl<F, T> IntoSignalSetter<T> for F
|
||||
impl<F, T, S> IntoSignalSetter<T, S> for F
|
||||
where
|
||||
F: Fn(T) + 'static + Send + Sync,
|
||||
T: Send + Sync,
|
||||
S: Storage<Box<dyn Fn(T) + Send + Sync>>,
|
||||
{
|
||||
fn into_signal_setter(self) -> SignalSetter<T> {
|
||||
fn into_signal_setter(self) -> SignalSetter<T, S> {
|
||||
SignalSetter::map(self)
|
||||
}
|
||||
}
|
||||
|
@ -845,22 +957,22 @@ pub mod write {
|
|||
/// assert_eq!(count.get(), 8);
|
||||
/// ```
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct SignalSetter<T>
|
||||
pub struct SignalSetter<T, S = SyncStorage>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
inner: SignalSetterTypes<T>,
|
||||
inner: SignalSetterTypes<T, S>,
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static std::panic::Location<'static>,
|
||||
}
|
||||
|
||||
impl<T> Clone for SignalSetter<T> {
|
||||
impl<T, S> Clone for SignalSetter<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default + 'static> Default for SignalSetter<T> {
|
||||
impl<T: Default + 'static, S> Default for SignalSetter<T, S> {
|
||||
#[track_caller]
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
@ -871,9 +983,13 @@ pub mod write {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for SignalSetter<T> {}
|
||||
impl<T, S> Copy for SignalSetter<T, S> {}
|
||||
|
||||
impl<T> Set for SignalSetter<T> {
|
||||
impl<T, S> Set for SignalSetter<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcWriteSignal<T>> + Storage<Box<dyn Fn(T) + Send + Sync>>,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn set(&self, new_value: Self::Value) {
|
||||
|
@ -903,61 +1019,26 @@ pub mod write {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> SignalSetter<T>
|
||||
impl<T, S> SignalSetter<T, S>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
S: Storage<Box<dyn Fn(T) + Send + Sync>>,
|
||||
{
|
||||
/// Wraps a signal-setting closure, i.e., any computation that sets one or more reactive signals.
|
||||
#[track_caller]
|
||||
pub fn map(mapped_setter: impl Fn(T) + Send + Sync + 'static) -> Self {
|
||||
Self {
|
||||
inner: SignalSetterTypes::Mapped(StoredValue::new(Box::new(
|
||||
mapped_setter,
|
||||
))),
|
||||
inner: SignalSetterTypes::Mapped(
|
||||
StoredValue::new_with_storage(Box::new(mapped_setter)),
|
||||
),
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: std::panic::Location::caller(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SignalSetter<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
/// Calls the setter function with the given value.
|
||||
///
|
||||
/// ```
|
||||
/// # use reactive_graph::wrappers::write::SignalSetter;
|
||||
/// # use reactive_graph::signal::signal;
|
||||
/// # use reactive_graph::prelude::*;
|
||||
/// let (count, set_count) = signal(2);
|
||||
/// let set_double_count = SignalSetter::map(move |n| set_count.set(n * 2));
|
||||
///
|
||||
/// // this function takes any kind of signal setter
|
||||
/// fn set_to_4(setter: &SignalSetter<i32>) {
|
||||
/// // ✅ calling the signal sets the value
|
||||
/// // can be `setter(4)` on nightly
|
||||
/// setter.set(4);
|
||||
/// }
|
||||
///
|
||||
/// set_to_4(&set_count.into());
|
||||
/// assert_eq!(count.get(), 4);
|
||||
/// set_to_4(&set_double_count);
|
||||
/// assert_eq!(count.get(), 8);
|
||||
/// ```
|
||||
impl<T, S> From<WriteSignal<T, S>> for SignalSetter<T, S> {
|
||||
#[track_caller]
|
||||
pub fn set(&self, value: T) {
|
||||
match &self.inner {
|
||||
SignalSetterTypes::Write(s) => s.set(value),
|
||||
SignalSetterTypes::Mapped(s) => s.with_value(|s| s(value)),
|
||||
SignalSetterTypes::Default => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<WriteSignal<T>> for SignalSetter<T> {
|
||||
#[track_caller]
|
||||
fn from(value: WriteSignal<T>) -> Self {
|
||||
fn from(value: WriteSignal<T, S>) -> Self {
|
||||
Self {
|
||||
inner: SignalSetterTypes::Write(value),
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -966,12 +1047,13 @@ pub mod write {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<RwSignal<T>> for SignalSetter<T>
|
||||
impl<T, S> From<RwSignal<T, S>> for SignalSetter<T, S>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
S: Storage<ArcRwSignal<T>> + Storage<ArcWriteSignal<T>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn from(value: RwSignal<T>) -> Self {
|
||||
fn from(value: RwSignal<T, S>) -> Self {
|
||||
Self {
|
||||
inner: SignalSetterTypes::Write(value.write_only()),
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -980,26 +1062,27 @@ pub mod write {
|
|||
}
|
||||
}
|
||||
|
||||
enum SignalSetterTypes<T>
|
||||
enum SignalSetterTypes<T, S = SyncStorage>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
Write(WriteSignal<T>),
|
||||
Mapped(StoredValue<Box<dyn Fn(T) + Send + Sync>>),
|
||||
Write(WriteSignal<T, S>),
|
||||
Mapped(StoredValue<Box<dyn Fn(T) + Send + Sync>, S>),
|
||||
Default,
|
||||
}
|
||||
|
||||
impl<T> Clone for SignalSetterTypes<T> {
|
||||
impl<T, S> Clone for SignalSetterTypes<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for SignalSetterTypes<T> {}
|
||||
impl<T, S> Copy for SignalSetterTypes<T, S> {}
|
||||
|
||||
impl<T> core::fmt::Debug for SignalSetterTypes<T>
|
||||
impl<T, S> core::fmt::Debug for SignalSetterTypes<T, S>
|
||||
where
|
||||
T: core::fmt::Debug,
|
||||
S: core::fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
|
@ -1014,7 +1097,7 @@ pub mod write {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for SignalSetterTypes<T>
|
||||
impl<T, S> PartialEq for SignalSetterTypes<T, S>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
|
@ -1027,5 +1110,5 @@ pub mod write {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for SignalSetterTypes<T> where T: PartialEq {}
|
||||
impl<T, S> Eq for SignalSetterTypes<T, S> where T: PartialEq {}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
ArcField, AtIndex, StoreField, Subfield,
|
||||
};
|
||||
use reactive_graph::{
|
||||
owner::StoredValue,
|
||||
owner::{Storage, StoredValue},
|
||||
signal::ArcTrigger,
|
||||
traits::{
|
||||
DefinedAt, IsDisposed, ReadUntracked, Track, Trigger, UntrackableGuard,
|
||||
|
@ -17,16 +17,19 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
pub struct Field<T>
|
||||
pub struct Field<T, S = SyncStorage>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
inner: StoredValue<ArcField<T>>,
|
||||
inner: StoredValue<ArcField<T>, S>,
|
||||
}
|
||||
|
||||
impl<T> StoreField<T> for Field<T> {
|
||||
impl<T, S> StoreField<T> for Field<T, S>
|
||||
where
|
||||
S: Storage<ArcField<T>>,
|
||||
{
|
||||
type Reader = StoreFieldReader<T>;
|
||||
type Writer = StoreFieldWriter<T>;
|
||||
|
||||
|
@ -53,9 +56,10 @@ impl<T> StoreField<T> for Field<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Inner, Prev, T> From<Subfield<Inner, Prev, T>> for Field<T>
|
||||
impl<Inner, Prev, T, S> From<Subfield<Inner, Prev, T>> for Field<T, S>
|
||||
where
|
||||
T: Send + Sync,
|
||||
S: Storage<ArcField<T>>,
|
||||
Subfield<Inner, Prev, T>: Clone,
|
||||
Inner: StoreField<Prev> + Send + Sync + 'static,
|
||||
Prev: 'static,
|
||||
|
@ -65,14 +69,15 @@ where
|
|||
Field {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(value.into()),
|
||||
inner: StoredValue::new_with_storage(value.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner, Prev> From<AtIndex<Inner, Prev>> for Field<Prev::Output>
|
||||
impl<Inner, Prev, S> From<AtIndex<Inner, Prev>> for Field<Prev::Output, S>
|
||||
where
|
||||
AtIndex<Inner, Prev>: Clone,
|
||||
S: Storage<ArcField<Prev::Output>>,
|
||||
Inner: StoreField<Prev> + Send + Sync + 'static,
|
||||
Prev: IndexMut<usize> + Send + Sync + 'static,
|
||||
Prev::Output: Sized + Send + Sync,
|
||||
|
@ -82,20 +87,20 @@ where
|
|||
Field {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(value.into()),
|
||||
inner: StoredValue::new_with_storage(value.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Field<T> {
|
||||
impl<T, S> Clone for Field<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for Field<T> {}
|
||||
impl<T, S> Copy for Field<T, S> {}
|
||||
|
||||
impl<T> DefinedAt for Field<T> {
|
||||
impl<T, S> DefinedAt for Field<T, S> {
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -108,7 +113,10 @@ impl<T> DefinedAt for Field<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Trigger for Field<T> {
|
||||
impl<T, S> Trigger for Field<T, S>
|
||||
where
|
||||
S: Storage<ArcField<T>>,
|
||||
{
|
||||
fn trigger(&self) {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.trigger();
|
||||
|
@ -116,7 +124,10 @@ impl<T> Trigger for Field<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Track for Field<T> {
|
||||
impl<T, S> Track for Field<T, S>
|
||||
where
|
||||
S: Storage<ArcField<T>>,
|
||||
{
|
||||
fn track(&self) {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.track();
|
||||
|
@ -124,7 +135,10 @@ impl<T> Track for Field<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> ReadUntracked for Field<T> {
|
||||
impl<T, S> ReadUntracked for Field<T, S>
|
||||
where
|
||||
S: Storage<ArcField<T>>,
|
||||
{
|
||||
type Value = StoreFieldReader<T>;
|
||||
|
||||
fn try_read_untracked(&self) -> Option<Self::Value> {
|
||||
|
@ -134,8 +148,8 @@ impl<T> ReadUntracked for Field<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> IsDisposed for Field<T> {
|
||||
impl<T, S> IsDisposed for Field<T, S> {
|
||||
fn is_disposed(&self) -> bool {
|
||||
!self.inner.exists()
|
||||
self.inner.is_disposed()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use reactive_graph::{
|
||||
owner::StoredValue,
|
||||
owner::{Storage, StoredValue},
|
||||
signal::{
|
||||
guards::{Plain, ReadGuard},
|
||||
ArcTrigger,
|
||||
|
@ -131,26 +131,30 @@ impl<T: 'static> Trigger for ArcStore<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Store<T> {
|
||||
pub struct Store<T, S = SyncStorage> {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
inner: StoredValue<ArcStore<T>>,
|
||||
inner: StoredValue<ArcStore<T>, S>,
|
||||
}
|
||||
|
||||
impl<T> Store<T>
|
||||
impl<T, S> Store<T, S>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
S: Storage<ArcStore<T>>,
|
||||
{
|
||||
pub fn new(value: T) -> Self {
|
||||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new(ArcStore::new(value)),
|
||||
inner: StoredValue::new_with_storage(ArcStore::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for Store<T> {
|
||||
impl<T: Debug, S> Debug for Store<T, S>
|
||||
where
|
||||
S: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut f = f.debug_struct("Store");
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -159,15 +163,15 @@ impl<T: Debug> Debug for Store<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Store<T> {
|
||||
impl<T, S> Clone for Store<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for Store<T> {}
|
||||
impl<T, S> Copy for Store<T, S> {}
|
||||
|
||||
impl<T> DefinedAt for Store<T> {
|
||||
impl<T, S> DefinedAt for Store<T, S> {
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -180,19 +184,20 @@ impl<T> DefinedAt for Store<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> IsDisposed for Store<T>
|
||||
impl<T, S> IsDisposed for Store<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn is_disposed(&self) -> bool {
|
||||
!self.inner.exists()
|
||||
self.inner.is_disposed()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ReadUntracked for Store<T>
|
||||
impl<T, S> ReadUntracked for Store<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcStore<T>>,
|
||||
{
|
||||
type Value = ReadGuard<T, Plain<T>>;
|
||||
|
||||
|
@ -203,7 +208,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Track for Store<T> {
|
||||
impl<T, S> Track for Store<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcStore<T>>,
|
||||
{
|
||||
fn track(&self) {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.track();
|
||||
|
@ -211,7 +220,11 @@ impl<T: 'static> Track for Store<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Trigger for Store<T> {
|
||||
impl<T, S> Trigger for Store<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcStore<T>>,
|
||||
{
|
||||
fn trigger(&self) {
|
||||
if let Some(inner) = self.inner.try_get_value() {
|
||||
inner.trigger();
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::{
|
|||
use guardian::ArcRwLockWriteGuardian;
|
||||
use or_poisoned::OrPoisoned;
|
||||
use reactive_graph::{
|
||||
owner::Storage,
|
||||
signal::{
|
||||
guards::{Plain, WriteGuard},
|
||||
ArcTrigger,
|
||||
|
@ -60,9 +61,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> StoreField<T> for Store<T>
|
||||
impl<T, S> StoreField<T> for Store<T, S>
|
||||
where
|
||||
T: 'static,
|
||||
S: Storage<ArcStore<T>>,
|
||||
{
|
||||
type Reader = Plain<T>;
|
||||
type Writer = WriteGuard<ArcTrigger, ArcRwLockWriteGuardian<T>>;
|
||||
|
|
|
@ -55,7 +55,7 @@ pub fn Router<Chil>(
|
|||
base: Option<Cow<'static, str>>,
|
||||
/// A signal that will be set while the navigation process is underway.
|
||||
#[prop(optional, into)]
|
||||
set_is_routing: Option<SignalSetter<bool>>,
|
||||
set_is_routing: Option<SignalSetter<bool, SyncStorage>>,
|
||||
// TODO trailing slashes
|
||||
///// How trailing slashes should be handled in [`Route`] paths.
|
||||
//#[prop(optional)]
|
||||
|
@ -106,7 +106,7 @@ pub(crate) struct RouterContext {
|
|||
pub current_url: ArcRwSignal<Url>,
|
||||
pub location: Location,
|
||||
pub state: ArcRwSignal<State>,
|
||||
pub set_is_routing: Option<SignalSetter<bool>>,
|
||||
pub set_is_routing: Option<SignalSetter<bool, SyncStorage>>,
|
||||
}
|
||||
|
||||
impl RouterContext {
|
||||
|
@ -471,7 +471,7 @@ pub fn provide_server_redirect(handler: impl Fn(&str) + Send + Sync + 'static) {
|
|||
pub fn RoutingProgress(
|
||||
/// Whether the router is currently loading the new page.
|
||||
#[prop(into)]
|
||||
is_routing: Signal<bool>,
|
||||
is_routing: Signal<bool, SyncStorage>,
|
||||
/// The maximum expected time for loading, which is used to
|
||||
/// calibrate the animation process.
|
||||
#[prop(optional, into)]
|
||||
|
|
|
@ -35,7 +35,7 @@ pub fn Form<A>(
|
|||
version: Option<RwSignal<usize>>,
|
||||
/// A signal that will be set if the form submission ends in an error.
|
||||
#[prop(optional)]
|
||||
error: Option<RwSignal<Option<Box<dyn Error>>>>,
|
||||
error: Option<RwSignal<Option<Box<dyn Error + Send + Sync>>>>,
|
||||
/// A callback will be called with the [`FormData`](web_sys::FormData) when the form is submitted.
|
||||
#[prop(optional)]
|
||||
on_form_data: Option<OnFormData>,
|
||||
|
@ -90,7 +90,7 @@ where
|
|||
action: ArcMemo<Option<String>>,
|
||||
enctype: Option<String>,
|
||||
version: Option<RwSignal<usize>>,
|
||||
error: Option<RwSignal<Option<Box<dyn Error>>>>,
|
||||
error: Option<RwSignal<Option<Box<dyn Error + Send + Sync>>>>,
|
||||
on_form_data: Option<OnFormData>,
|
||||
on_response: Option<OnResponse>,
|
||||
on_error: Option<OnError>,
|
||||
|
|
|
@ -452,11 +452,13 @@ where
|
|||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
mod stable {
|
||||
macro_rules! class_signal {
|
||||
macro_rules! class_signal_arena {
|
||||
($sig:ident) => {
|
||||
impl<C, R> IntoClass<R> for $sig<C>
|
||||
impl<C, R, S> IntoClass<R> for $sig<C, S>
|
||||
where
|
||||
C: IntoClass<R> + Clone + Send + Sync + 'static,
|
||||
$sig<C, S>: Get<Value = C>,
|
||||
S: Send + Sync + 'static,
|
||||
C: IntoClass<R> + Send + Sync + Clone + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
|
@ -504,8 +506,10 @@ mod stable {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R> IntoClass<R> for (&'static str, $sig<bool>)
|
||||
impl<R, S> IntoClass<R> for (&'static str, $sig<bool, S>)
|
||||
where
|
||||
$sig<bool, S>: Get<Value = bool>,
|
||||
S: Send + 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
@ -563,10 +567,11 @@ mod stable {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! class_signal_unsend {
|
||||
macro_rules! class_signal {
|
||||
($sig:ident) => {
|
||||
impl<C, R> IntoClass<R> for $sig<C>
|
||||
where
|
||||
$sig<C>: Get<Value = C>,
|
||||
C: IntoClass<R> + Send + Sync + Clone + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
|
@ -617,6 +622,7 @@ mod stable {
|
|||
|
||||
impl<R> IntoClass<R> for (&'static str, $sig<bool>)
|
||||
where
|
||||
$sig<bool>: Get<Value = bool>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
@ -683,13 +689,13 @@ mod stable {
|
|||
wrappers::read::{ArcSignal, MaybeSignal, Signal},
|
||||
};
|
||||
|
||||
class_signal!(RwSignal);
|
||||
class_signal!(ReadSignal);
|
||||
class_signal!(Memo);
|
||||
class_signal!(Signal);
|
||||
class_signal!(MaybeSignal);
|
||||
class_signal_unsend!(ArcRwSignal);
|
||||
class_signal_unsend!(ArcReadSignal);
|
||||
class_signal_arena!(RwSignal);
|
||||
class_signal_arena!(ReadSignal);
|
||||
class_signal_arena!(Memo);
|
||||
class_signal_arena!(Signal);
|
||||
class_signal_arena!(MaybeSignal);
|
||||
class_signal!(ArcRwSignal);
|
||||
class_signal!(ArcReadSignal);
|
||||
class_signal!(ArcMemo);
|
||||
class_signal!(ArcSignal);
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ mod stable {
|
|||
($sig:ident) => {
|
||||
impl<V, R> InnerHtmlValue<R> for $sig<V>
|
||||
where
|
||||
$sig<V>: Get<Value = V>,
|
||||
V: InnerHtmlValue<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
|
@ -144,10 +145,12 @@ mod stable {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! inner_html_signal_unsend {
|
||||
macro_rules! inner_html_signal_arena {
|
||||
($sig:ident) => {
|
||||
impl<V, R> InnerHtmlValue<R> for $sig<V>
|
||||
impl<V, R, S> InnerHtmlValue<R> for $sig<V, S>
|
||||
where
|
||||
$sig<V, S>: Get<Value = V>,
|
||||
S: Send + Sync + 'static,
|
||||
V: InnerHtmlValue<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
|
@ -198,13 +201,13 @@ mod stable {
|
|||
};
|
||||
}
|
||||
|
||||
inner_html_signal!(RwSignal);
|
||||
inner_html_signal!(ReadSignal);
|
||||
inner_html_signal!(Memo);
|
||||
inner_html_signal!(Signal);
|
||||
inner_html_signal!(MaybeSignal);
|
||||
inner_html_signal_unsend!(ArcRwSignal);
|
||||
inner_html_signal_unsend!(ArcReadSignal);
|
||||
inner_html_signal_arena!(RwSignal);
|
||||
inner_html_signal_arena!(ReadSignal);
|
||||
inner_html_signal_arena!(Memo);
|
||||
inner_html_signal_arena!(Signal);
|
||||
inner_html_signal_arena!(MaybeSignal);
|
||||
inner_html_signal!(ArcRwSignal);
|
||||
inner_html_signal!(ArcReadSignal);
|
||||
inner_html_signal!(ArcMemo);
|
||||
inner_html_signal!(ArcSignal);
|
||||
}
|
||||
|
|
|
@ -505,6 +505,7 @@ mod stable {
|
|||
($sig:ident $dry_resolve:literal) => {
|
||||
impl<V, R> Render<R> for $sig<V>
|
||||
where
|
||||
$sig<V>: Get<Value = V>,
|
||||
V: Render<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
|
||||
|
@ -528,6 +529,7 @@ mod stable {
|
|||
|
||||
impl<V, R> AddAnyAttr<R> for $sig<V>
|
||||
where
|
||||
$sig<V>: Get<Value = V>,
|
||||
V: RenderHtml<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
R: Renderer + 'static,
|
||||
|
@ -547,6 +549,7 @@ mod stable {
|
|||
|
||||
impl<V, R> RenderHtml<R> for $sig<V>
|
||||
where
|
||||
$sig<V>: Get<Value = V>,
|
||||
V: RenderHtml<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
|
||||
|
@ -611,6 +614,7 @@ mod stable {
|
|||
|
||||
impl<V, R> AttributeValue<R> for $sig<V>
|
||||
where
|
||||
$sig<V>: Get<Value = V>,
|
||||
V: AttributeValue<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
R: Renderer,
|
||||
|
@ -668,10 +672,12 @@ mod stable {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! signal_impl_unsend {
|
||||
macro_rules! signal_impl_arena {
|
||||
($sig:ident $dry_resolve:literal) => {
|
||||
impl<V, R> Render<R> for $sig<V>
|
||||
impl<V, R, S> Render<R> for $sig<V, S>
|
||||
where
|
||||
$sig<V, S>: Get<Value = V>,
|
||||
S: Send + Sync + 'static,
|
||||
V: Render<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
|
||||
|
@ -693,13 +699,15 @@ mod stable {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V, R> AddAnyAttr<R> for $sig<V>
|
||||
impl<V, R, S> AddAnyAttr<R> for $sig<V, S>
|
||||
where
|
||||
$sig<V, S>: Get<Value = V>,
|
||||
S: Send + Sync + 'static,
|
||||
V: RenderHtml<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute<R>> = $sig<V>;
|
||||
type Output<SomeNewAttr: Attribute<R>> = $sig<V, S>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
|
@ -712,8 +720,10 @@ mod stable {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V, R> RenderHtml<R> for $sig<V>
|
||||
impl<V, R, S> RenderHtml<R> for $sig<V, S>
|
||||
where
|
||||
$sig<V, S>: Get<Value = V>,
|
||||
S: Send + Sync + 'static,
|
||||
V: RenderHtml<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
|
||||
|
@ -776,8 +786,10 @@ mod stable {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V, R> AttributeValue<R> for $sig<V>
|
||||
impl<V, R, S> AttributeValue<R> for $sig<V, S>
|
||||
where
|
||||
$sig<V, S>: Get<Value = V>,
|
||||
S: Send + Sync + 'static,
|
||||
V: AttributeValue<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: Renderer,
|
||||
|
@ -835,13 +847,13 @@ mod stable {
|
|||
};
|
||||
}
|
||||
|
||||
signal_impl!(RwSignal false);
|
||||
signal_impl!(ReadSignal false);
|
||||
signal_impl!(Memo true);
|
||||
signal_impl!(Signal true);
|
||||
signal_impl!(MaybeSignal true);
|
||||
signal_impl_unsend!(ArcRwSignal false);
|
||||
signal_impl_unsend!(ArcReadSignal false);
|
||||
signal_impl_arena!(RwSignal false);
|
||||
signal_impl_arena!(ReadSignal false);
|
||||
signal_impl_arena!(Memo true);
|
||||
signal_impl_arena!(Signal true);
|
||||
signal_impl_arena!(MaybeSignal true);
|
||||
signal_impl!(ArcRwSignal false);
|
||||
signal_impl!(ArcReadSignal false);
|
||||
signal_impl!(ArcMemo false);
|
||||
signal_impl!(ArcSignal true);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::{
|
|||
renderer::{dom::Dom, Renderer},
|
||||
};
|
||||
use reactive_graph::{
|
||||
owner::SyncStorage,
|
||||
signal::RwSignal,
|
||||
traits::{DefinedAt, Set, Track, WithUntracked},
|
||||
};
|
||||
|
@ -11,7 +12,7 @@ use wasm_bindgen::JsCast;
|
|||
|
||||
/// A reactive reference to a DOM node that can be used with the `node_ref` attribute.
|
||||
#[derive(Debug)]
|
||||
pub struct NodeRef<E>(RwSignal<Option<SendWrapper<E::Output>>>)
|
||||
pub struct NodeRef<E>(RwSignal<Option<SendWrapper<E::Output>>, SyncStorage>)
|
||||
where
|
||||
E: ElementType,
|
||||
E::Output: 'static;
|
||||
|
|
|
@ -101,6 +101,7 @@ mod stable {
|
|||
($sig:ident) => {
|
||||
impl<V, R> IntoProperty<R> for $sig<V>
|
||||
where
|
||||
$sig<V>: Get<Value = V>,
|
||||
V: IntoProperty<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
|
@ -140,10 +141,12 @@ mod stable {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! property_signal_unsend {
|
||||
macro_rules! property_signal_arena {
|
||||
($sig:ident) => {
|
||||
impl<V, R> IntoProperty<R> for $sig<V>
|
||||
impl<V, R, S> IntoProperty<R> for $sig<V, S>
|
||||
where
|
||||
$sig<V, S>: Get<Value = V>,
|
||||
S: Send + Sync + 'static,
|
||||
V: IntoProperty<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
|
@ -183,13 +186,13 @@ mod stable {
|
|||
};
|
||||
}
|
||||
|
||||
property_signal!(RwSignal);
|
||||
property_signal!(ReadSignal);
|
||||
property_signal!(Memo);
|
||||
property_signal!(Signal);
|
||||
property_signal!(MaybeSignal);
|
||||
property_signal_unsend!(ArcRwSignal);
|
||||
property_signal_unsend!(ArcReadSignal);
|
||||
property_signal_arena!(RwSignal);
|
||||
property_signal_arena!(ReadSignal);
|
||||
property_signal_arena!(Memo);
|
||||
property_signal_arena!(Signal);
|
||||
property_signal_arena!(MaybeSignal);
|
||||
property_signal!(ArcRwSignal);
|
||||
property_signal!(ArcReadSignal);
|
||||
property_signal!(ArcMemo);
|
||||
property_signal!(ArcSignal);
|
||||
}
|
||||
|
|
|
@ -201,6 +201,7 @@ mod stable {
|
|||
($sig:ident) => {
|
||||
impl<C, R> IntoStyle<R> for $sig<C>
|
||||
where
|
||||
$sig<C>: Get<Value = C>,
|
||||
C: IntoStyle<R> + Clone + Send + Sync + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
|
@ -247,6 +248,7 @@ mod stable {
|
|||
|
||||
impl<R, S> IntoStyle<R> for (&'static str, $sig<S>)
|
||||
where
|
||||
$sig<S>: Get<Value = S>,
|
||||
S: Into<Cow<'static, str>> + Send + Sync + Clone + 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
|
@ -301,10 +303,12 @@ mod stable {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! style_signal_unsend {
|
||||
macro_rules! style_signal_arena {
|
||||
($sig:ident) => {
|
||||
impl<C, R> IntoStyle<R> for $sig<C>
|
||||
impl<C, R, S> IntoStyle<R> for $sig<C, S>
|
||||
where
|
||||
$sig<C, S>: Get<Value = C>,
|
||||
S: Send + Sync + 'static,
|
||||
C: IntoStyle<R> + Send + Sync + Clone + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
|
@ -349,8 +353,10 @@ mod stable {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R, S> IntoStyle<R> for (&'static str, $sig<S>)
|
||||
impl<R, S, St> IntoStyle<R> for (&'static str, $sig<S, St>)
|
||||
where
|
||||
$sig<S, St>: Get<Value = S>,
|
||||
St: Send + Sync + 'static,
|
||||
S: Into<Cow<'static, str>> + Send + Sync + Clone + 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
|
@ -415,13 +421,13 @@ mod stable {
|
|||
};
|
||||
use std::borrow::Cow;
|
||||
|
||||
style_signal!(RwSignal);
|
||||
style_signal!(ReadSignal);
|
||||
style_signal!(Memo);
|
||||
style_signal!(Signal);
|
||||
style_signal!(MaybeSignal);
|
||||
style_signal_unsend!(ArcRwSignal);
|
||||
style_signal_unsend!(ArcReadSignal);
|
||||
style_signal_arena!(RwSignal);
|
||||
style_signal_arena!(ReadSignal);
|
||||
style_signal_arena!(Memo);
|
||||
style_signal_arena!(Signal);
|
||||
style_signal_arena!(MaybeSignal);
|
||||
style_signal!(ArcRwSignal);
|
||||
style_signal!(ArcReadSignal);
|
||||
style_signal!(ArcMemo);
|
||||
style_signal!(ArcSignal);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue