feat: add a copyable Trigger type (closes #2901) (#2939)

This commit is contained in:
jk 2024-09-09 01:39:40 +02:00 committed by GitHub
parent 4e4fb8ab10
commit 73f0207a7d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 168 additions and 64 deletions

View file

@ -18,7 +18,7 @@ use crate::{
ArcTrigger, ArcTrigger,
}, },
traits::{ traits::{
DefinedAt, ReadUntracked, Track, Trigger, UntrackableGuard, Writeable, DefinedAt, Notify, ReadUntracked, Track, UntrackableGuard, Writeable,
}, },
transition::AsyncTransition, transition::AsyncTransition,
}; };
@ -580,8 +580,8 @@ impl<T: 'static> ReadUntracked for ArcAsyncDerived<T> {
} }
} }
impl<T: 'static> Trigger for ArcAsyncDerived<T> { impl<T: 'static> Notify for ArcAsyncDerived<T> {
fn trigger(&self) { fn notify(&self) {
Self::notify_subs(&self.wakers, &self.inner, &self.loading, None); Self::notify_subs(&self.wakers, &self.inner, &self.loading, None);
} }
} }

View file

@ -7,7 +7,7 @@ use crate::{
owner::{FromLocal, LocalStorage, Storage, StoredValue, SyncStorage}, owner::{FromLocal, LocalStorage, Storage, StoredValue, SyncStorage},
signal::guards::{AsyncPlain, ReadGuard, WriteGuard}, signal::guards::{AsyncPlain, ReadGuard, WriteGuard},
traits::{ traits::{
DefinedAt, Dispose, ReadUntracked, Trigger, UntrackableGuard, Writeable, DefinedAt, Dispose, Notify, ReadUntracked, UntrackableGuard, Writeable,
}, },
unwrap_signal, unwrap_signal,
}; };
@ -291,13 +291,13 @@ where
} }
} }
impl<T, S> Trigger for AsyncDerived<T, S> impl<T, S> Notify for AsyncDerived<T, S>
where where
T: 'static, T: 'static,
S: Storage<ArcAsyncDerived<T>>, S: Storage<ArcAsyncDerived<T>>,
{ {
fn trigger(&self) { fn notify(&self) {
self.inner.try_with_value(|inner| inner.trigger()); self.inner.try_with_value(|inner| inner.notify());
} }
} }

View file

@ -9,6 +9,7 @@ pub mod guards;
mod read; mod read;
mod rw; mod rw;
mod subscriber_traits; mod subscriber_traits;
mod trigger;
mod write; mod write;
use crate::owner::LocalStorage; use crate::owner::LocalStorage;
@ -18,6 +19,7 @@ pub use arc_trigger::*;
pub use arc_write::*; pub use arc_write::*;
pub use read::*; pub use read::*;
pub use rw::*; pub use rw::*;
pub use trigger::*;
pub use write::*; pub use write::*;
/// Creates a reference-counted signal. /// Creates a reference-counted signal.

View file

@ -5,7 +5,7 @@ use super::{
}; };
use crate::{ use crate::{
graph::{ReactiveNode, SubscriberSet}, graph::{ReactiveNode, SubscriberSet},
prelude::{IsDisposed, Trigger}, prelude::{IsDisposed, Notify},
traits::{DefinedAt, ReadUntracked, UntrackableGuard, Writeable}, traits::{DefinedAt, ReadUntracked, UntrackableGuard, Writeable},
}; };
use core::fmt::{Debug, Formatter, Result}; use core::fmt::{Debug, Formatter, Result};
@ -247,8 +247,8 @@ impl<T: 'static> ReadUntracked for ArcRwSignal<T> {
} }
} }
impl<T> Trigger for ArcRwSignal<T> { impl<T> Notify for ArcRwSignal<T> {
fn trigger(&self) { fn notify(&self) {
self.mark_dirty(); self.mark_dirty();
} }
} }

View file

@ -1,7 +1,7 @@
use super::subscriber_traits::AsSubscriberSet; use super::subscriber_traits::AsSubscriberSet;
use crate::{ use crate::{
graph::{ReactiveNode, SubscriberSet}, graph::{ReactiveNode, SubscriberSet},
traits::{DefinedAt, IsDisposed, Trigger}, traits::{DefinedAt, IsDisposed, Notify},
}; };
use std::{ use std::{
fmt::{Debug, Formatter, Result}, fmt::{Debug, Formatter, Result},
@ -83,8 +83,8 @@ impl DefinedAt for ArcTrigger {
} }
} }
impl Trigger for ArcTrigger { impl Notify for ArcTrigger {
fn trigger(&self) { fn notify(&self) {
self.inner.mark_dirty(); self.inner.mark_dirty();
} }
} }

View file

@ -1,7 +1,7 @@
use super::guards::{UntrackedWriteGuard, WriteGuard}; use super::guards::{UntrackedWriteGuard, WriteGuard};
use crate::{ use crate::{
graph::{ReactiveNode, SubscriberSet}, graph::{ReactiveNode, SubscriberSet},
prelude::{IsDisposed, Trigger}, prelude::{IsDisposed, Notify},
traits::{DefinedAt, UntrackableGuard, Writeable}, traits::{DefinedAt, UntrackableGuard, Writeable},
}; };
use core::fmt::{Debug, Formatter, Result}; use core::fmt::{Debug, Formatter, Result};
@ -116,8 +116,8 @@ impl<T> IsDisposed for ArcWriteSignal<T> {
} }
} }
impl<T> Trigger for ArcWriteSignal<T> { impl<T> Notify for ArcWriteSignal<T> {
fn trigger(&self) { fn notify(&self) {
self.inner.mark_dirty(); self.inner.mark_dirty();
} }
} }

View file

@ -2,7 +2,7 @@
use crate::{ use crate::{
computed::BlockingLock, computed::BlockingLock,
traits::{Trigger, UntrackableGuard}, traits::{Notify, UntrackableGuard},
}; };
use core::fmt::Debug; use core::fmt::Debug;
use guardian::{ArcRwLockReadGuardian, ArcRwLockWriteGuardian}; use guardian::{ArcRwLockReadGuardian, ArcRwLockWriteGuardian};
@ -259,7 +259,7 @@ where
#[derive(Debug)] #[derive(Debug)]
pub struct WriteGuard<S, G> pub struct WriteGuard<S, G>
where where
S: Trigger, S: Notify,
{ {
pub(crate) triggerable: Option<S>, pub(crate) triggerable: Option<S>,
pub(crate) guard: Option<G>, pub(crate) guard: Option<G>,
@ -267,7 +267,7 @@ where
impl<S, G> WriteGuard<S, G> impl<S, G> WriteGuard<S, G>
where where
S: Trigger, S: Notify,
{ {
/// Creates a new guard from the inner mutable guard type, and the signal that should be /// Creates a new guard from the inner mutable guard type, and the signal that should be
/// triggered on drop. /// triggered on drop.
@ -281,7 +281,7 @@ where
impl<S, G> UntrackableGuard for WriteGuard<S, G> impl<S, G> UntrackableGuard for WriteGuard<S, G>
where where
S: Trigger, S: Notify,
G: DerefMut, G: DerefMut,
{ {
/// Removes the triggerable type, so that it is no longer notifies when dropped. /// Removes the triggerable type, so that it is no longer notifies when dropped.
@ -292,7 +292,7 @@ where
impl<S, G> Deref for WriteGuard<S, G> impl<S, G> Deref for WriteGuard<S, G>
where where
S: Trigger, S: Notify,
G: Deref, G: Deref,
{ {
type Target = G::Target; type Target = G::Target;
@ -310,7 +310,7 @@ where
impl<S, G> DerefMut for WriteGuard<S, G> impl<S, G> DerefMut for WriteGuard<S, G>
where where
S: Trigger, S: Notify,
G: DerefMut, G: DerefMut,
{ {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
@ -354,7 +354,7 @@ impl<T> DerefMut for UntrackedWriteGuard<T> {
// Dropping the write guard will notify dependencies. // Dropping the write guard will notify dependencies.
impl<S, T> Drop for WriteGuard<S, T> impl<S, T> Drop for WriteGuard<S, T>
where where
S: Trigger, S: Notify,
{ {
fn drop(&mut self) { fn drop(&mut self) {
// first, drop the inner guard // first, drop the inner guard
@ -362,7 +362,7 @@ where
// then, notify about a change // then, notify about a change
if let Some(triggerable) = self.triggerable.as_ref() { if let Some(triggerable) = self.triggerable.as_ref() {
triggerable.trigger(); triggerable.notify();
} }
} }
} }

View file

@ -8,7 +8,7 @@ use crate::{
owner::{FromLocal, LocalStorage, Storage, StoredValue, SyncStorage}, owner::{FromLocal, LocalStorage, Storage, StoredValue, SyncStorage},
signal::guards::{UntrackedWriteGuard, WriteGuard}, signal::guards::{UntrackedWriteGuard, WriteGuard},
traits::{ traits::{
DefinedAt, Dispose, IsDisposed, ReadUntracked, Trigger, DefinedAt, Dispose, IsDisposed, Notify, ReadUntracked,
UntrackableGuard, Writeable, UntrackableGuard, Writeable,
}, },
unwrap_signal, unwrap_signal,
@ -340,11 +340,11 @@ where
} }
} }
impl<T, S> Trigger for RwSignal<T, S> impl<T, S> Notify for RwSignal<T, S>
where where
S: Storage<ArcRwSignal<T>>, S: Storage<ArcRwSignal<T>>,
{ {
fn trigger(&self) { fn notify(&self) {
self.mark_dirty(); self.mark_dirty();
} }
} }

View file

@ -0,0 +1,103 @@
use super::{subscriber_traits::AsSubscriberSet, ArcTrigger};
use crate::{
graph::{ReactiveNode, SubscriberSet},
owner::StoredValue,
traits::{DefinedAt, Dispose, IsDisposed, Notify},
};
use std::{
fmt::{Debug, Formatter, Result},
panic::Location,
sync::{Arc, RwLock},
};
/// A trigger is a data-less signal with the sole purpose of notifying other reactive code of a change.
///
/// This can be useful for when using external data not stored in signals, for example.
///
/// This is an arena-allocated Trigger, which is `Copy` and is disposed when its reactive
/// [`Owner`](crate::owner::Owner) cleans up. For a reference-counted trigger that lives
/// as long as a reference to it is alive, see [`ArcTrigger`].
pub struct Trigger {
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static Location<'static>,
pub(crate) inner: StoredValue<ArcTrigger>,
}
impl Trigger {
/// Creates a new trigger.
#[track_caller]
pub fn new() -> Self {
Self {
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: StoredValue::new(ArcTrigger::new()),
}
}
}
impl Default for Trigger {
fn default() -> Self {
Self::new()
}
}
impl Clone for Trigger {
#[track_caller]
fn clone(&self) -> Self {
*self
}
}
impl Copy for Trigger {}
impl Debug for Trigger {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("Trigger").finish()
}
}
impl Dispose for Trigger {
fn dispose(self) {
self.inner.dispose()
}
}
impl IsDisposed for Trigger {
#[inline(always)]
fn is_disposed(&self) -> bool {
self.inner.is_disposed()
}
}
impl AsSubscriberSet for Trigger {
type Output = Arc<RwLock<SubscriberSet>>;
#[inline(always)]
fn as_subscriber_set(&self) -> Option<Self::Output> {
self.inner
.try_get_value()
.and_then(|arc_trigger| arc_trigger.as_subscriber_set())
}
}
impl DefinedAt for Trigger {
#[inline(always)]
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(debug_assertions))]
{
None
}
}
}
impl Notify for Trigger {
fn notify(&self) {
if let Some(inner) = self.inner.try_get_value() {
inner.mark_dirty();
}
}
}

View file

@ -2,7 +2,7 @@ use super::{guards::WriteGuard, ArcWriteSignal};
use crate::{ use crate::{
owner::{Storage, StoredValue, SyncStorage}, owner::{Storage, StoredValue, SyncStorage},
traits::{ traits::{
DefinedAt, Dispose, IsDisposed, Trigger, UntrackableGuard, Writeable, DefinedAt, Dispose, IsDisposed, Notify, UntrackableGuard, Writeable,
}, },
}; };
use core::fmt::Debug; use core::fmt::Debug;
@ -116,14 +116,14 @@ impl<T, S> IsDisposed for WriteSignal<T, S> {
} }
} }
impl<T, S> Trigger for WriteSignal<T, S> impl<T, S> Notify for WriteSignal<T, S>
where where
T: 'static, T: 'static,
S: Storage<ArcWriteSignal<T>>, S: Storage<ArcWriteSignal<T>>,
{ {
fn trigger(&self) { fn notify(&self) {
if let Some(inner) = self.inner.try_get_value() { if let Some(inner) = self.inner.try_get_value() {
inner.trigger(); inner.notify();
} }
} }
} }

View file

@ -209,7 +209,7 @@ pub trait UntrackableGuard: DerefMut {
/// Gives mutable access to a signal's value through a guard type. When the guard is dropped, the /// Gives mutable access to a signal's value through a guard type. When the guard is dropped, the
/// signal's subscribers will be notified. /// signal's subscribers will be notified.
pub trait Writeable: Sized + DefinedAt + Trigger { pub trait Writeable: Sized + DefinedAt + Notify {
/// The type of the signal's value. /// The type of the signal's value.
type Value: Sized + 'static; type Value: Sized + 'static;
@ -381,9 +381,9 @@ where
} }
/// Notifies subscribers of a change in this signal. /// Notifies subscribers of a change in this signal.
pub trait Trigger { pub trait Notify {
/// Notifies subscribers of a change in this signal. /// Notifies subscribers of a change in this signal.
fn trigger(&self); fn notify(&self);
} }
/// Updates the value of a signal by applying a function that updates it in place, /// Updates the value of a signal by applying a function that updates it in place,

View file

@ -5,7 +5,7 @@ use crate::{
use reactive_graph::{ use reactive_graph::{
signal::ArcTrigger, signal::ArcTrigger,
traits::{ traits::{
DefinedAt, IsDisposed, ReadUntracked, Track, Trigger, UntrackableGuard, DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,
}, },
}; };
use std::{ use std::{
@ -180,9 +180,9 @@ impl<T> DefinedAt for ArcField<T> {
} }
} }
impl<T> Trigger for ArcField<T> { impl<T> Notify for ArcField<T> {
fn trigger(&self) { fn notify(&self) {
self.trigger.trigger(); self.trigger.notify();
} }
} }

View file

@ -6,7 +6,7 @@ use crate::{
use reactive_graph::{ use reactive_graph::{
owner::{Storage, StoredValue, SyncStorage}, owner::{Storage, StoredValue, SyncStorage},
signal::ArcTrigger, signal::ArcTrigger,
traits::{DefinedAt, IsDisposed, ReadUntracked, Track, Trigger}, traits::{DefinedAt, IsDisposed, Notify, ReadUntracked, Track},
unwrap_signal, unwrap_signal,
}; };
use std::{ops::IndexMut, panic::Location}; use std::{ops::IndexMut, panic::Location};
@ -108,13 +108,13 @@ impl<T, S> DefinedAt for Field<T, S> {
} }
} }
impl<T, S> Trigger for Field<T, S> impl<T, S> Notify for Field<T, S>
where where
S: Storage<ArcField<T>>, S: Storage<ArcField<T>>,
{ {
fn trigger(&self) { fn notify(&self) {
if let Some(inner) = self.inner.try_get_value() { if let Some(inner) = self.inner.try_get_value() {
inner.trigger(); inner.notify();
} }
} }
} }

View file

@ -8,7 +8,7 @@ use reactive_graph::{
ArcTrigger, ArcTrigger,
}, },
traits::{ traits::{
DefinedAt, IsDisposed, ReadUntracked, Track, Trigger, UntrackableGuard, DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,
Writeable, Writeable,
}, },
}; };
@ -136,15 +136,15 @@ where
} }
} }
impl<Inner, Prev> Trigger for AtIndex<Inner, Prev> impl<Inner, Prev> Notify for AtIndex<Inner, Prev>
where where
Inner: StoreField<Value = Prev>, Inner: StoreField<Value = Prev>,
Prev: IndexMut<usize> + 'static, Prev: IndexMut<usize> + 'static,
Prev::Output: Sized, Prev::Output: Sized,
{ {
fn trigger(&self) { fn notify(&self) {
let trigger = self.get_trigger(self.path().into_iter().collect()); let trigger = self.get_trigger(self.path().into_iter().collect());
trigger.trigger(); trigger.notify();
} }
} }

View file

@ -4,7 +4,7 @@ use reactive_graph::{
guards::{Plain, ReadGuard}, guards::{Plain, ReadGuard},
ArcTrigger, ArcTrigger,
}, },
traits::{DefinedAt, IsDisposed, ReadUntracked, Track, Trigger}, traits::{DefinedAt, IsDisposed, Notify, ReadUntracked, Track},
}; };
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::{ use std::{
@ -123,14 +123,13 @@ where
impl<T: 'static> Track for ArcStore<T> { impl<T: 'static> Track for ArcStore<T> {
fn track(&self) { fn track(&self) {
self.get_trigger(Default::default()).trigger(); self.get_trigger(Default::default()).notify();
} }
} }
impl<T: 'static> Trigger for ArcStore<T> { impl<T: 'static> Notify for ArcStore<T> {
fn trigger(&self) { fn notify(&self) {
self.get_trigger(self.path().into_iter().collect()) self.get_trigger(self.path().into_iter().collect()).notify();
.trigger();
} }
} }
@ -235,14 +234,14 @@ where
} }
} }
impl<T, S> Trigger for Store<T, S> impl<T, S> Notify for Store<T, S>
where where
T: 'static, T: 'static,
S: Storage<ArcStore<T>>, S: Storage<ArcStore<T>>,
{ {
fn trigger(&self) { fn notify(&self) {
if let Some(inner) = self.inner.try_get_value() { if let Some(inner) = self.inner.try_get_value() {
inner.trigger(); inner.notify();
} }
} }
} }

View file

@ -1,6 +1,6 @@
use crate::{path::StorePath, StoreField}; use crate::{path::StorePath, StoreField};
use itertools::{EitherOrBoth, Itertools}; use itertools::{EitherOrBoth, Itertools};
use reactive_graph::traits::{Trigger, UntrackableGuard}; use reactive_graph::traits::{Notify, UntrackableGuard};
use std::{ use std::{
borrow::Cow, borrow::Cow,
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
@ -33,7 +33,7 @@ where
writer.untrack(); writer.untrack();
let mut notify = |path: &StorePath| { let mut notify = |path: &StorePath| {
println!("notifying on {path:?}"); println!("notifying on {path:?}");
self.get_trigger(path.to_owned()).trigger(); self.get_trigger(path.to_owned()).notify();
}; };
writer.patch_field(new, &path, &mut notify); writer.patch_field(new, &path, &mut notify);
} }

View file

@ -8,7 +8,7 @@ use reactive_graph::{
ArcTrigger, ArcTrigger,
}, },
traits::{ traits::{
DefinedAt, IsDisposed, ReadUntracked, Track, Trigger, UntrackableGuard, DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,
Writeable, Writeable,
}, },
}; };
@ -130,14 +130,14 @@ where
} }
} }
impl<Inner, Prev, T> Trigger for Subfield<Inner, Prev, T> impl<Inner, Prev, T> Notify for Subfield<Inner, Prev, T>
where where
Inner: StoreField<Value = Prev>, Inner: StoreField<Value = Prev>,
Prev: 'static, Prev: 'static,
{ {
fn trigger(&self) { fn notify(&self) {
let trigger = self.get_trigger(self.path().into_iter().collect()); let trigger = self.get_trigger(self.path().into_iter().collect());
trigger.trigger(); trigger.notify();
} }
} }

View file

@ -15,7 +15,7 @@ use reactive_graph::{
computed::{ArcMemo, ScopedFuture}, computed::{ArcMemo, ScopedFuture},
owner::{provide_context, use_context, Owner}, owner::{provide_context, use_context, Owner},
signal::{ArcRwSignal, ArcTrigger}, signal::{ArcRwSignal, ArcTrigger},
traits::{Get, GetUntracked, ReadUntracked, Set, Track, Trigger}, traits::{Get, GetUntracked, Notify, ReadUntracked, Set, Track},
wrappers::write::SignalSetter, wrappers::write::SignalSetter,
}; };
use send_wrapper::SendWrapper; use send_wrapper::SendWrapper;
@ -124,7 +124,7 @@ where
ScopedFuture::new(async move { ScopedFuture::new(async move {
let triggers = join_all(loaders).await; let triggers = join_all(loaders).await;
for trigger in triggers { for trigger in triggers {
trigger.trigger(); trigger.notify();
} }
matched_view.rebuild(&mut *view.borrow_mut()); matched_view.rebuild(&mut *view.borrow_mut());
}) })
@ -179,7 +179,7 @@ where
let triggers = join_all(loaders).await; let triggers = join_all(loaders).await;
// tell each one of the outlet triggers that it's ready // tell each one of the outlet triggers that it's ready
for trigger in triggers { for trigger in triggers {
trigger.trigger(); trigger.notify();
} }
if let Some(loc) = location { if let Some(loc) = location {
loc.ready_to_complete(); loc.ready_to_complete();