make guard types more nestable/flexible so that we can implement render traits on any of them

This commit is contained in:
Greg Johnston 2024-02-17 14:54:38 -05:00
parent 44a0a0a93a
commit 4df42cbc60
22 changed files with 251 additions and 183 deletions

View file

@ -30,7 +30,7 @@ pub fn async_example() -> impl IntoView {
let times = move || {
trigger.track();
async move { (a2.await.to_string(), "test") } //{ (a2.await.to_string(), " and ", b2.await.to_string()) }
async move { (b2.await, a2.await, " and ") }
.suspend()
.with_fallback("Loading...")
.track()

View file

@ -16,6 +16,7 @@ guardian = "1"
[dev-dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "time", "macros"] }
any_spawner = { workspace = true, features = ["tokio"] }
[features]
nightly = []

View file

@ -4,7 +4,7 @@ use crate::{
AnySource, AnySubscriber, ReactiveNode, Source, Subscriber,
ToAnySource, ToAnySubscriber,
},
signal::MappedSignalReadGuard,
signal::guards::{Mapped, Plain, ReadGuard},
traits::{DefinedAt, ReadUntracked},
};
use core::fmt::Debug;
@ -159,15 +159,16 @@ impl<T: Send + Sync + 'static> Subscriber for ArcMemo<T> {
}
impl<T: Send + Sync + 'static> ReadUntracked for ArcMemo<T> {
type Value = MappedSignalReadGuard<MemoInner<T>, T>;
type Value = ReadGuard<T, Mapped<Plain<MemoInner<T>>, T>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
self.update_if_necessary();
MappedSignalReadGuard::try_new(Arc::clone(&self.inner), |t| {
Mapped::try_new(Arc::clone(&self.inner), |t| {
// safe to unwrap here because update_if_necessary
// guarantees the value is Some
t.value.as_ref().unwrap()
})
.map(ReadGuard::new)
}
}

View file

@ -9,7 +9,7 @@ use crate::{
SubscriberSet, ToAnySource, ToAnySubscriber,
},
owner::Owner,
signal::SignalReadGuard,
signal::guards::{Plain, ReadGuard},
traits::{DefinedAt, ReadUntracked},
};
use any_spawner::Executor;
@ -222,10 +222,10 @@ impl<T: 'static> ArcAsyncDerived<T> {
}
impl<T: 'static> ReadUntracked for ArcAsyncDerived<T> {
type Value = SignalReadGuard<AsyncState<T>>;
type Value = ReadGuard<AsyncState<T>, Plain<AsyncState<T>>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
SignalReadGuard::try_new(Arc::clone(&self.value))
Plain::try_new(Arc::clone(&self.value)).map(ReadGuard::new)
}
}

View file

@ -7,7 +7,7 @@ use crate::{
ToAnySource, ToAnySubscriber,
},
owner::{Stored, StoredData},
signal::{MappedSignalReadGuard, SignalReadGuard},
signal::guards::{Mapped, Plain, ReadGuard},
traits::{DefinedAt, ReadUntracked},
unwrap_signal,
};
@ -136,7 +136,7 @@ impl<T: Send + Sync + 'static> DefinedAt for AsyncDerived<T> {
}
impl<T: Send + Sync + Clone + 'static> IntoFuture for AsyncDerived<T> {
type Output = MappedSignalReadGuard<AsyncState<T>, T>;
type Output = ReadGuard<T, Mapped<Plain<AsyncState<T>>, T>>;
type IntoFuture = AsyncDerivedFuture<T>;
#[track_caller]
@ -147,7 +147,7 @@ impl<T: Send + Sync + Clone + 'static> IntoFuture for AsyncDerived<T> {
}
impl<T: Send + Sync + 'static> ReadUntracked for AsyncDerived<T> {
type Value = SignalReadGuard<AsyncState<T>>;
type Value = ReadGuard<AsyncState<T>, Plain<AsyncState<T>>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
self.get_value().map(|inner| inner.read_untracked())

View file

@ -1,7 +1,7 @@
use super::{ArcAsyncDerived, AsyncState};
use crate::{
graph::{AnySource, ToAnySource},
signal::{MappedSignalReadGuard, SignalReadGuard},
signal::guards::{Mapped, Plain, ReadGuard},
traits::Track,
};
use or_poisoned::OrPoisoned;
@ -45,7 +45,7 @@ pub struct AsyncDerivedFuture<T> {
}
impl<T: 'static> IntoFuture for ArcAsyncDerived<T> {
type Output = MappedSignalReadGuard<AsyncState<T>, T>;
type Output = ReadGuard<T, Mapped<Plain<AsyncState<T>>, T>>;
type IntoFuture = AsyncDerivedFuture<T>;
fn into_future(self) -> Self::IntoFuture {
@ -58,29 +58,27 @@ impl<T: 'static> IntoFuture for ArcAsyncDerived<T> {
}
impl<T: 'static> Future for AsyncDerivedFuture<T> {
type Output = MappedSignalReadGuard<AsyncState<T>, T>;
type Output = ReadGuard<T, Mapped<Plain<AsyncState<T>>, T>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let waker = cx.waker();
self.source.track();
let value = SignalReadGuard::try_new(Arc::clone(&self.value))
.expect("lock poisoned");
let value =
Plain::try_new(Arc::clone(&self.value)).expect("lock poisoned");
match &*value {
AsyncState::Loading | AsyncState::Reloading(_) => {
self.wakers.write().or_poisoned().push(waker.clone());
Poll::Pending
}
AsyncState::Complete(_) => Poll::Ready(
MappedSignalReadGuard::try_new(
Arc::clone(&self.value),
|inner| match inner {
AsyncState::Complete(_) => {
Poll::Ready(ReadGuard::new(Mapped::new(value, |inner| {
match inner {
AsyncState::Complete(value) => value,
// we've just checked this value is Complete
_ => unreachable!(),
},
)
.expect("lock poisoned"),
),
}
})))
}
}
}
}

View file

@ -6,7 +6,10 @@ use crate::{
owner::Owner,
};
use or_poisoned::OrPoisoned;
use std::sync::{Arc, RwLock};
use std::{
fmt::Debug,
sync::{Arc, RwLock},
};
pub struct MemoInner<T> {
pub(crate) value: Option<T>,
@ -20,6 +23,12 @@ pub struct MemoInner<T> {
pub(crate) any_subscriber: AnySubscriber,
}
impl<T> Debug for MemoInner<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MemoInner").finish_non_exhaustive()
}
}
impl<T: Send + Sync + 'static> MemoInner<T> {
#[allow(clippy::type_complexity)]
pub fn new(

View file

@ -1,7 +1,7 @@
use super::{inner::MemoInner, ArcMemo};
use crate::{
owner::{Stored, StoredData},
signal::MappedSignalReadGuard,
signal::guards::{Mapped, Plain, ReadGuard},
traits::{DefinedAt, ReadUntracked, Track},
};
use std::{fmt::Debug, panic::Location};
@ -81,7 +81,7 @@ impl<T: Send + Sync + 'static> Track for Memo<T> {
}
impl<T: Send + Sync + 'static> ReadUntracked for Memo<T> {
type Value = MappedSignalReadGuard<MemoInner<T>, T>;
type Value = ReadGuard<T, Mapped<Plain<MemoInner<T>>, T>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
self.get_value().map(|inner| inner.read_untracked())

View file

@ -1 +0,0 @@
pub struct SignalReadGuard<'a>(RwLockReadGuard<'a>);

View file

@ -16,7 +16,7 @@
//! use reactive_graph::{
//! computed::ArcMemo,
//! effect::Effect,
//! prelude::{Readable, Set},
//! prelude::{Read, Set},
//! signal::ArcRwSignal,
//! };
//!

View file

@ -1,7 +1,7 @@
mod arc_read;
mod arc_rw;
mod arc_write;
mod guards;
pub mod guards;
mod read;
mod rw;
mod subscriber_traits;
@ -10,7 +10,6 @@ mod write;
pub use arc_read::*;
pub use arc_rw::*;
pub use arc_write::*;
pub use guards::*;
pub use read::*;
pub use rw::*;
pub use write::*;

View file

@ -1,4 +1,7 @@
use super::{subscriber_traits::AsSubscriberSet, SignalReadGuard};
use super::{
guards::{Plain, ReadGuard},
subscriber_traits::AsSubscriberSet,
};
use crate::{
graph::SubscriberSet,
traits::{DefinedAt, IsDisposed, ReadUntracked},
@ -83,10 +86,10 @@ impl<T> AsSubscriberSet for ArcReadSignal<T> {
}
impl<T: 'static> ReadUntracked for ArcReadSignal<T> {
type Value = SignalReadGuard<T>;
type Value = ReadGuard<T, Plain<T>>;
#[track_caller]
fn try_read_untracked(&self) -> Option<Self::Value> {
SignalReadGuard::try_new(Arc::clone(&self.value))
Plain::try_new(Arc::clone(&self.value)).map(ReadGuard::new)
}
}

View file

@ -1,6 +1,7 @@
use super::{
subscriber_traits::AsSubscriberSet, ArcReadSignal, ArcWriteSignal,
SignalReadGuard, SignalUntrackedWriteGuard, SignalWriteGuard,
guards::{Plain, ReadGuard, UntrackedWriteGuard, WriteGuard},
subscriber_traits::AsSubscriberSet,
ArcReadSignal, ArcWriteSignal,
};
use crate::{
graph::{ReactiveNode, SubscriberSet},
@ -113,10 +114,10 @@ impl<T> AsSubscriberSet for ArcRwSignal<T> {
}
impl<T: 'static> ReadUntracked for ArcRwSignal<T> {
type Value = SignalReadGuard<T>;
type Value = ReadGuard<T, Plain<T>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
SignalReadGuard::try_new(Arc::clone(&self.value))
Plain::try_new(Arc::clone(&self.value)).map(ReadGuard::new)
}
}
@ -129,16 +130,16 @@ impl<T> Trigger for ArcRwSignal<T> {
impl<T> Writeable for ArcRwSignal<T> {
type Value = T;
fn try_write(&self) -> Option<SignalWriteGuard<'_, Self, Self::Value>> {
fn try_write(&self) -> Option<WriteGuard<'_, Self, Self::Value>> {
self.value
.write()
.ok()
.map(|guard| SignalWriteGuard::new(self, guard))
.map(|guard| WriteGuard::new(self, guard))
}
fn try_write_untracked(
&self,
) -> Option<SignalUntrackedWriteGuard<'_, Self::Value>> {
self.value.write().ok().map(SignalUntrackedWriteGuard::from)
) -> Option<UntrackedWriteGuard<'_, Self::Value>> {
self.value.write().ok().map(UntrackedWriteGuard::from)
}
}

View file

@ -1,4 +1,4 @@
use super::{SignalUntrackedWriteGuard, SignalWriteGuard};
use super::guards::{UntrackedWriteGuard, WriteGuard};
use crate::{
graph::{ReactiveNode, SubscriberSet},
prelude::{IsDisposed, Trigger},
@ -83,16 +83,16 @@ impl<T> Trigger for ArcWriteSignal<T> {
impl<T> Writeable for ArcWriteSignal<T> {
type Value = T;
fn try_write(&self) -> Option<SignalWriteGuard<'_, Self, Self::Value>> {
fn try_write(&self) -> Option<WriteGuard<'_, Self, Self::Value>> {
self.value
.write()
.ok()
.map(|guard| SignalWriteGuard::new(self, guard))
.map(|guard| WriteGuard::new(self, guard))
}
fn try_write_untracked(
&self,
) -> Option<SignalUntrackedWriteGuard<'_, Self::Value>> {
self.value.write().ok().map(SignalUntrackedWriteGuard::from)
) -> Option<UntrackedWriteGuard<'_, Self::Value>> {
self.value.write().ok().map(UntrackedWriteGuard::from)
}
}

View file

@ -3,29 +3,76 @@ use core::fmt::Debug;
use guardian::ArcRwLockReadGuardian;
use std::{
fmt::Display,
marker::PhantomData,
ops::{Deref, DerefMut},
sync::{Arc, RwLock, RwLockWriteGuard},
};
pub struct SignalReadGuard<T: 'static> {
#[derive(Debug)]
pub struct ReadGuard<T, Inner> {
ty: PhantomData<T>,
inner: Inner,
}
impl<T, Inner> ReadGuard<T, Inner> {
pub fn new(inner: Inner) -> Self {
Self {
inner,
ty: PhantomData,
}
}
}
impl<T, Inner> Deref for ReadGuard<T, Inner>
where
Inner: Deref<Target = T>,
{
type Target = T;
fn deref(&self) -> &Self::Target {
self.inner.deref()
}
}
impl<T, Inner> PartialEq<T> for ReadGuard<T, Inner>
where
Inner: Deref<Target = T>,
T: PartialEq,
{
fn eq(&self, other: &Inner::Target) -> bool {
self.deref() == other
}
}
impl<T, Inner> Display for ReadGuard<T, Inner>
where
Inner: Deref<Target = T>,
T: Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&**self, f)
}
}
pub struct Plain<T: 'static> {
guard: ArcRwLockReadGuardian<T>,
}
impl<T: 'static> Debug for SignalReadGuard<T> {
impl<T: 'static> Debug for Plain<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SignalReadGuard").finish()
f.debug_struct("Plain").finish()
}
}
impl<T: 'static> SignalReadGuard<T> {
pub fn try_new(inner: Arc<RwLock<T>>) -> Option<Self> {
impl<T: 'static> Plain<T> {
pub(crate) fn try_new(inner: Arc<RwLock<T>>) -> Option<Self> {
ArcRwLockReadGuardian::take(inner)
.ok()
.map(|guard| SignalReadGuard { guard })
.map(|guard| Plain { guard })
}
}
impl<T> Deref for SignalReadGuard<T> {
impl<T> Deref for Plain<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
@ -33,74 +80,92 @@ impl<T> Deref for SignalReadGuard<T> {
}
}
impl<T: PartialEq> PartialEq for SignalReadGuard<T> {
impl<T: PartialEq> PartialEq for Plain<T> {
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<T: PartialEq> PartialEq<T> for SignalReadGuard<T> {
impl<T: PartialEq> PartialEq<T> for Plain<T> {
fn eq(&self, other: &T) -> bool {
**self == *other
}
}
impl<T: Display> Display for SignalReadGuard<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&**self, f)
}
}
pub struct MappedSignalReadGuard<T: 'static, U> {
guard: ArcRwLockReadGuardian<T>,
map_fn: fn(&T) -> &U,
}
impl<T: 'static, U> Debug for MappedSignalReadGuard<T, U> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MappedSignalReadGuard").finish()
}
}
impl<T: 'static, U> MappedSignalReadGuard<T, U> {
pub fn try_new(
inner: Arc<RwLock<T>>,
map_fn: fn(&T) -> &U,
) -> Option<Self> {
ArcRwLockReadGuardian::take(inner)
.ok()
.map(|guard| MappedSignalReadGuard { guard, map_fn })
}
}
impl<T, U> Deref for MappedSignalReadGuard<T, U> {
type Target = U;
fn deref(&self) -> &Self::Target {
(self.map_fn)(self.guard.deref())
}
}
impl<T, U: PartialEq> PartialEq for MappedSignalReadGuard<T, U> {
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<T, U: PartialEq> PartialEq<U> for MappedSignalReadGuard<T, U> {
fn eq(&self, other: &U) -> bool {
**self == *other
}
}
impl<T, U: Display> Display for MappedSignalReadGuard<T, U> {
impl<T: Display> Display for Plain<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&**self, f)
}
}
#[derive(Debug)]
pub struct SignalWriteGuard<'a, S, T>
pub struct Mapped<Inner, U>
where
Inner: Deref,
{
inner: Inner,
map_fn: fn(&Inner::Target) -> &U,
}
impl<Inner, U> Mapped<Inner, U>
where
Inner: Deref,
{
pub(crate) fn new(inner: Inner, map_fn: fn(&Inner::Target) -> &U) -> Self {
Self { inner, map_fn }
}
}
impl<T: 'static, U> Mapped<Plain<T>, U> {
pub(crate) fn try_new(
inner: Arc<RwLock<T>>,
map_fn: fn(&T) -> &U,
) -> Option<Self> {
let inner = Plain::try_new(inner)?;
Some(Self { inner, map_fn })
}
}
impl<Inner, U> Deref for Mapped<Inner, U>
where
Inner: Deref,
{
type Target = U;
fn deref(&self) -> &Self::Target {
(self.map_fn)(self.inner.deref())
}
}
impl<Inner, U: PartialEq> PartialEq for Mapped<Inner, U>
where
Inner: Deref,
{
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<Inner, U: PartialEq> PartialEq<U> for Mapped<Inner, U>
where
Inner: Deref,
{
fn eq(&self, other: &U) -> bool {
**self == *other
}
}
impl<Inner, U: Display> Display for Mapped<Inner, U>
where
Inner: Deref,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&**self, f)
}
}
#[derive(Debug)]
pub struct WriteGuard<'a, S, T>
where
S: Trigger,
{
@ -108,7 +173,7 @@ where
guard: Option<RwLockWriteGuard<'a, T>>,
}
impl<'a, S, T> SignalWriteGuard<'a, S, T>
impl<'a, S, T> WriteGuard<'a, S, T>
where
S: Trigger,
{
@ -120,7 +185,7 @@ where
}
}
impl<'a, S, T> Deref for SignalWriteGuard<'a, S, T>
impl<'a, S, T> Deref for WriteGuard<'a, S, T>
where
S: Trigger,
{
@ -137,7 +202,7 @@ where
}
}
impl<'a, S, T> DerefMut for SignalWriteGuard<'a, S, T>
impl<'a, S, T> DerefMut for WriteGuard<'a, S, T>
where
S: Trigger,
{
@ -153,15 +218,15 @@ where
}
#[derive(Debug)]
pub struct SignalUntrackedWriteGuard<'a, T>(RwLockWriteGuard<'a, T>);
pub struct UntrackedWriteGuard<'a, T>(RwLockWriteGuard<'a, T>);
impl<'a, T> From<RwLockWriteGuard<'a, T>> for SignalUntrackedWriteGuard<'a, T> {
impl<'a, T> From<RwLockWriteGuard<'a, T>> for UntrackedWriteGuard<'a, T> {
fn from(value: RwLockWriteGuard<'a, T>) -> Self {
Self(value)
}
}
impl<'a, T> Deref for SignalUntrackedWriteGuard<'a, T> {
impl<'a, T> Deref for UntrackedWriteGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
@ -169,14 +234,14 @@ impl<'a, T> Deref for SignalUntrackedWriteGuard<'a, T> {
}
}
impl<'a, T> DerefMut for SignalUntrackedWriteGuard<'a, T> {
impl<'a, T> DerefMut for UntrackedWriteGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.deref_mut()
}
}
// Dropping the write guard will notify dependencies.
impl<'a, S, T> Drop for SignalWriteGuard<'a, S, T>
impl<'a, S, T> Drop for WriteGuard<'a, S, T>
where
S: Trigger,
{

View file

@ -1,5 +1,7 @@
use super::{
subscriber_traits::AsSubscriberSet, ArcReadSignal, SignalReadGuard,
guards::{Plain, ReadGuard},
subscriber_traits::AsSubscriberSet,
ArcReadSignal,
};
use crate::{
graph::SubscriberSet,
@ -78,7 +80,7 @@ impl<T: Send + Sync + 'static> AsSubscriberSet for ReadSignal<T> {
}
impl<T: Send + Sync + 'static> ReadUntracked for ReadSignal<T> {
type Value = SignalReadGuard<T>;
type Value = ReadGuard<T, Plain<T>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
self.get_value().map(|inner| inner.read_untracked())

View file

@ -1,6 +1,7 @@
use super::{
subscriber_traits::AsSubscriberSet, ArcRwSignal, ReadSignal,
SignalReadGuard, WriteSignal,
guards::{Plain, ReadGuard},
subscriber_traits::AsSubscriberSet,
ArcRwSignal, ReadSignal, WriteSignal,
};
use crate::{
graph::{ReactiveNode, SubscriberSet},
@ -147,7 +148,7 @@ impl<T: Send + Sync + 'static> AsSubscriberSet for RwSignal<T> {
}
impl<T: Send + Sync + 'static> ReadUntracked for RwSignal<T> {
type Value = SignalReadGuard<T>;
type Value = ReadGuard<T, Plain<T>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
self.get_value().map(|inner| inner.read_untracked())

View file

@ -50,7 +50,7 @@
use crate::{
graph::{Observer, Source, Subscriber, ToAnySource},
signal::{SignalUntrackedWriteGuard, SignalWriteGuard},
signal::guards::{UntrackedWriteGuard, WriteGuard},
};
use std::{ops::Deref, panic::Location};
@ -138,17 +138,17 @@ where
pub trait Writeable: Sized + DefinedAt + Trigger {
type Value: Sized;
fn try_write(&self) -> Option<SignalWriteGuard<'_, Self, Self::Value>>;
fn try_write(&self) -> Option<WriteGuard<'_, Self, Self::Value>>;
fn try_write_untracked(
&self,
) -> Option<SignalUntrackedWriteGuard<'_, Self::Value>>;
) -> Option<UntrackedWriteGuard<'_, Self::Value>>;
fn write(&self) -> SignalWriteGuard<'_, Self, Self::Value> {
fn write(&self) -> WriteGuard<'_, Self, Self::Value> {
self.try_write().unwrap_or_else(unwrap_signal!(self))
}
fn write_untracked(&self) -> SignalUntrackedWriteGuard<'_, Self::Value> {
fn write_untracked(&self) -> UntrackedWriteGuard<'_, Self::Value> {
self.try_write_untracked()
.unwrap_or_else(unwrap_signal!(self))
}

View file

@ -5,7 +5,6 @@ use reactive_graph::{
traits::{Get, Read, Set, With, WithUntracked},
};
#[cfg(feature = "tokio")]
#[tokio::test]
async fn arc_async_derived_calculates_eagerly() {
use std::time::Duration;
@ -22,7 +21,6 @@ async fn arc_async_derived_calculates_eagerly() {
std::mem::forget(value);
}
#[cfg(feature = "tokio")]
#[tokio::test]
async fn arc_async_derived_tracks_signal_change() {
use std::time::Duration;
@ -46,7 +44,6 @@ async fn arc_async_derived_tracks_signal_change() {
std::mem::forget(value);
}
#[cfg(feature = "tokio")]
#[tokio::test]
async fn async_derived_calculates_eagerly() {
use std::time::Duration;
@ -62,7 +59,6 @@ async fn async_derived_calculates_eagerly() {
assert_eq!(*value.await, 42);
}
#[cfg(feature = "tokio")]
#[tokio::test]
async fn async_derived_tracks_signal_change() {
use std::time::Duration;
@ -85,8 +81,10 @@ async fn async_derived_tracks_signal_change() {
assert_eq!(value.await, 50);
}
#[test]
fn read_signal_traits_on_arc() {
#[tokio::test]
async fn read_signal_traits_on_arc() {
_ = Executor::init_tokio();
let value = ArcAsyncDerived::new(move || async {});
assert_eq!(value.read(), AsyncState::Loading);
assert_eq!(value.with_untracked(|n| *n), AsyncState::Loading);
@ -94,8 +92,10 @@ fn read_signal_traits_on_arc() {
assert_eq!(value.get(), AsyncState::Loading);
}
#[test]
fn read_signal_traits_on_arena() {
#[tokio::test]
async fn read_signal_traits_on_arena() {
_ = Executor::init_tokio();
let value = AsyncDerived::new(move || async {});
println!("{:?}", value.read());
assert_eq!(value.read(), AsyncState::Loading);

View file

@ -13,10 +13,9 @@ pub async fn tick() {
tokio::time::sleep(std::time::Duration::from_micros(1)).await;
}
#[cfg(feature = "futures-executor")]
#[test]
fn render_effect_runs() {
_ = Executor::init_futures_executor();
#[tokio::test]
async fn render_effect_runs() {
_ = Executor::init_tokio();
Executor::spawn(async {
let a = RwSignal::new(-1);
@ -45,10 +44,9 @@ fn render_effect_runs() {
});
}
#[cfg(feature = "futures-executor")]
#[test]
fn effect_runs() {
_ = Executor::init_futures_executor();
#[tokio::test]
async fn effect_runs() {
_ = Executor::init_tokio();
Executor::spawn(async {
let a = RwSignal::new(-1);
@ -74,10 +72,10 @@ fn effect_runs() {
assert_eq!(b.read().unwrap().as_str(), "Value is 1");
});
}
#[cfg(feature = "futures-executor")]
#[test]
fn dynamic_dependencies() {
_ = Executor::init_futures_executor();
#[tokio::test]
async fn dynamic_dependencies() {
_ = Executor::init_tokio();
Executor::spawn(async {
let first = RwSignal::new("Greg");
@ -137,20 +135,3 @@ fn dynamic_dependencies() {
assert_eq!(*combined_count.read().unwrap(), 5);
});
}
/*
#[cfg(feature = "futures-executor")]
#[test]
fn effect_runs() {
_ = Executor::init_futures_executor();
Executor::spawn(async {});
}
#[cfg(feature = "futures-executor")]
#[test]
fn effect_runs() {
_ = Executor::init_futures_executor();
Executor::spawn(async {});
}*/

View file

@ -169,10 +169,9 @@ fn diamond_problem() {
assert_eq!(*combined_count.read().unwrap(), 1);
}
#[cfg(feature = "tokio")]
#[tokio::test]
async fn dynamic_dependencies() {
_ = Executor::init_futures_executor();
_ = Executor::init_tokio();
let first = RwSignal::new("Greg");
let last = RwSignal::new("Johnston");

View file

@ -9,7 +9,7 @@ use crate::{
PositionState, Render, ToTemplate,
},
};
use reactive_graph::signal::SignalReadGuard;
use reactive_graph::signal::guards::ReadGuard;
use std::{
fmt::Write,
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
@ -18,6 +18,7 @@ use std::{
NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64,
NonZeroU8, NonZeroUsize,
},
ops::Deref,
};
// any changes here should also be made in src/view/primitives.rs
@ -26,9 +27,9 @@ macro_rules! render_primitive {
($($child_type:ty),* $(,)?) => {
$(
paste::paste! {
pub struct [<SignalReadGuard $child_type:camel State>]<R>(R::Text, $child_type) where R: Renderer;
pub struct [<ReadGuard $child_type:camel State>]<R>(R::Text, $child_type) where R: Renderer;
impl<'a, R: Renderer> Mountable<R> for [<SignalReadGuard $child_type:camel State>]<R> {
impl<'a, R: Renderer> Mountable<R> for [<ReadGuard $child_type:camel State>]<R> {
fn unmount(&mut self) {
self.0.unmount()
}
@ -51,16 +52,18 @@ macro_rules! render_primitive {
}
}
impl<'a, R: Renderer> Render<R> for SignalReadGuard<$child_type> {
type State = [<SignalReadGuard $child_type:camel State>]<R>;
impl<'a, G, R: Renderer> Render<R> for ReadGuard<$child_type, G>
where G: Deref<Target = $child_type>
{
type State = [<ReadGuard $child_type:camel State>]<R>;
fn build(self) -> Self::State {
let node = R::create_text_node(&self.to_string());
[<SignalReadGuard $child_type:camel State>](node, *self)
[<ReadGuard $child_type:camel State>](node, *self)
}
fn rebuild(self, state: &mut Self::State) {
let [<SignalReadGuard $child_type:camel State>](node, this) = state;
let [<ReadGuard $child_type:camel State>](node, this) = state;
if &self != this {
R::set_text(node, &self.to_string());
*this = *self;
@ -68,13 +71,14 @@ macro_rules! render_primitive {
}
}
impl<'a> InfallibleRender for SignalReadGuard<$child_type> {}
impl<'a, G> InfallibleRender for ReadGuard<$child_type, G> {}
impl<'a, R> RenderHtml<R> for SignalReadGuard<$child_type>
impl<'a, G, R> RenderHtml<R> for ReadGuard<$child_type, G>
where
R: Renderer,
R::Node: Clone,
R::Element: Clone,
G: Deref<Target = $child_type>
{
const MIN_LENGTH: usize = 0;
@ -112,11 +116,12 @@ macro_rules! render_primitive {
}
position.set(Position::NextChildAfterText);
[<SignalReadGuard $child_type:camel State>](node, *self)
[<ReadGuard $child_type:camel State>](node, *self)
}
}
impl<'a> ToTemplate for SignalReadGuard<$child_type> {
impl<'a, G> ToTemplate for ReadGuard<$child_type, G>
{
const TEMPLATE: &'static str = " <!>";
fn to_template(
@ -176,24 +181,27 @@ render_primitive![
];
// strings
pub struct SignalReadGuardStringState<R: Renderer> {
pub struct ReadGuardStringState<R: Renderer> {
node: R::Text,
str: String,
}
impl<R: Renderer> Render<R> for SignalReadGuard<String> {
type State = SignalReadGuardStringState<R>;
impl<G, R: Renderer> Render<R> for ReadGuard<String, G>
where
G: Deref<Target = String>,
{
type State = ReadGuardStringState<R>;
fn build(self) -> Self::State {
let node = R::create_text_node(&self);
SignalReadGuardStringState {
ReadGuardStringState {
node,
str: self.to_string(),
}
}
fn rebuild(self, state: &mut Self::State) {
let SignalReadGuardStringState { node, str } = state;
let ReadGuardStringState { node, str } = state;
if *self != *str {
R::set_text(node, &self);
str.clear();
@ -202,13 +210,14 @@ impl<R: Renderer> Render<R> for SignalReadGuard<String> {
}
}
impl InfallibleRender for SignalReadGuard<String> {}
impl<G> InfallibleRender for ReadGuard<String, G> {}
impl<R> RenderHtml<R> for SignalReadGuard<String>
impl<G, R> RenderHtml<R> for ReadGuard<String, G>
where
R: Renderer,
R::Node: Clone,
R::Element: Clone,
G: Deref<Target = String>,
{
const MIN_LENGTH: usize = 0;
@ -224,14 +233,14 @@ where
let this: &str = self.as_ref();
let StrState { node, .. } =
this.hydrate::<FROM_SERVER>(cursor, position);
SignalReadGuardStringState {
ReadGuardStringState {
node,
str: self.to_string(),
}
}
}
impl ToTemplate for SignalReadGuard<String> {
impl<G> ToTemplate for ReadGuard<String, G> {
const TEMPLATE: &'static str = <&str as ToTemplate>::TEMPLATE;
fn to_template(
@ -247,7 +256,7 @@ impl ToTemplate for SignalReadGuard<String> {
}
}
impl<R: Renderer> Mountable<R> for SignalReadGuardStringState<R> {
impl<R: Renderer> Mountable<R> for ReadGuardStringState<R> {
fn unmount(&mut self) {
self.node.unmount()
}