feat: allow !Send signals

This commit is contained in:
Greg Johnston 2024-07-23 08:45:59 -04:00
parent d4ec5e187b
commit 1f2b13a976
37 changed files with 1323 additions and 710 deletions

View file

@ -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>

View file

@ -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"]

View file

@ -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()

View file

@ -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();

View file

@ -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

View file

@ -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,

View file

@ -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(),
}

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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> {}

View file

@ -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> {

View file

@ -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);
}
}

View file

@ -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()
}
}

View file

@ -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,
{

View file

@ -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)),
}
}
}

View file

@ -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];

View file

@ -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,

View file

@ -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;

View file

@ -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,
{

View file

@ -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>,

View file

@ -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)
}

View file

@ -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))
}
}

View file

@ -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))
}
}

View file

@ -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>> {

View file

@ -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 {}
}

View file

@ -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()
}
}

View file

@ -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();

View file

@ -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>>;

View file

@ -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)]

View file

@ -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>,

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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);
}