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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@
use crate::{
computed::BlockingLock,
traits::{Trigger, UntrackableGuard},
traits::{Notify, UntrackableGuard},
};
use core::fmt::Debug;
use guardian::{ArcRwLockReadGuardian, ArcRwLockWriteGuardian};
@ -259,7 +259,7 @@ where
#[derive(Debug)]
pub struct WriteGuard<S, G>
where
S: Trigger,
S: Notify,
{
pub(crate) triggerable: Option<S>,
pub(crate) guard: Option<G>,
@ -267,7 +267,7 @@ where
impl<S, G> WriteGuard<S, G>
where
S: Trigger,
S: Notify,
{
/// Creates a new guard from the inner mutable guard type, and the signal that should be
/// triggered on drop.
@ -281,7 +281,7 @@ where
impl<S, G> UntrackableGuard for WriteGuard<S, G>
where
S: Trigger,
S: Notify,
G: DerefMut,
{
/// 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>
where
S: Trigger,
S: Notify,
G: Deref,
{
type Target = G::Target;
@ -310,7 +310,7 @@ where
impl<S, G> DerefMut for WriteGuard<S, G>
where
S: Trigger,
S: Notify,
G: DerefMut,
{
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.
impl<S, T> Drop for WriteGuard<S, T>
where
S: Trigger,
S: Notify,
{
fn drop(&mut self) {
// first, drop the inner guard
@ -362,7 +362,7 @@ where
// then, notify about a change
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},
signal::guards::{UntrackedWriteGuard, WriteGuard},
traits::{
DefinedAt, Dispose, IsDisposed, ReadUntracked, Trigger,
DefinedAt, Dispose, IsDisposed, Notify, ReadUntracked,
UntrackableGuard, Writeable,
},
unwrap_signal,
@ -340,11 +340,11 @@ where
}
}
impl<T, S> Trigger for RwSignal<T, S>
impl<T, S> Notify for RwSignal<T, S>
where
S: Storage<ArcRwSignal<T>>,
{
fn trigger(&self) {
fn notify(&self) {
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::{
owner::{Storage, StoredValue, SyncStorage},
traits::{
DefinedAt, Dispose, IsDisposed, Trigger, UntrackableGuard, Writeable,
DefinedAt, Dispose, IsDisposed, Notify, UntrackableGuard, Writeable,
},
};
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
T: 'static,
S: Storage<ArcWriteSignal<T>>,
{
fn trigger(&self) {
fn notify(&self) {
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
/// 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.
type Value: Sized + 'static;
@ -381,9 +381,9 @@ where
}
/// Notifies subscribers of a change in this signal.
pub trait Trigger {
pub trait Notify {
/// 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,

View file

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

View file

@ -6,7 +6,7 @@ use crate::{
use reactive_graph::{
owner::{Storage, StoredValue, SyncStorage},
signal::ArcTrigger,
traits::{DefinedAt, IsDisposed, ReadUntracked, Track, Trigger},
traits::{DefinedAt, IsDisposed, Notify, ReadUntracked, Track},
unwrap_signal,
};
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
S: Storage<ArcField<T>>,
{
fn trigger(&self) {
fn notify(&self) {
if let Some(inner) = self.inner.try_get_value() {
inner.trigger();
inner.notify();
}
}
}

View file

@ -8,7 +8,7 @@ use reactive_graph::{
ArcTrigger,
},
traits::{
DefinedAt, IsDisposed, ReadUntracked, Track, Trigger, UntrackableGuard,
DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,
Writeable,
},
};
@ -136,15 +136,15 @@ where
}
}
impl<Inner, Prev> Trigger for AtIndex<Inner, Prev>
impl<Inner, Prev> Notify for AtIndex<Inner, Prev>
where
Inner: StoreField<Value = Prev>,
Prev: IndexMut<usize> + 'static,
Prev::Output: Sized,
{
fn trigger(&self) {
fn notify(&self) {
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},
ArcTrigger,
},
traits::{DefinedAt, IsDisposed, ReadUntracked, Track, Trigger},
traits::{DefinedAt, IsDisposed, Notify, ReadUntracked, Track},
};
use rustc_hash::FxHashMap;
use std::{
@ -123,14 +123,13 @@ where
impl<T: 'static> Track for ArcStore<T> {
fn track(&self) {
self.get_trigger(Default::default()).trigger();
self.get_trigger(Default::default()).notify();
}
}
impl<T: 'static> Trigger for ArcStore<T> {
fn trigger(&self) {
self.get_trigger(self.path().into_iter().collect())
.trigger();
impl<T: 'static> Notify for ArcStore<T> {
fn notify(&self) {
self.get_trigger(self.path().into_iter().collect()).notify();
}
}
@ -235,14 +234,14 @@ where
}
}
impl<T, S> Trigger for Store<T, S>
impl<T, S> Notify for Store<T, S>
where
T: 'static,
S: Storage<ArcStore<T>>,
{
fn trigger(&self) {
fn notify(&self) {
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 itertools::{EitherOrBoth, Itertools};
use reactive_graph::traits::{Trigger, UntrackableGuard};
use reactive_graph::traits::{Notify, UntrackableGuard};
use std::{
borrow::Cow,
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
@ -33,7 +33,7 @@ where
writer.untrack();
let mut notify = |path: &StorePath| {
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);
}

View file

@ -8,7 +8,7 @@ use reactive_graph::{
ArcTrigger,
},
traits::{
DefinedAt, IsDisposed, ReadUntracked, Track, Trigger, UntrackableGuard,
DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,
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
Inner: StoreField<Value = Prev>,
Prev: 'static,
{
fn trigger(&self) {
fn notify(&self) {
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},
owner::{provide_context, use_context, Owner},
signal::{ArcRwSignal, ArcTrigger},
traits::{Get, GetUntracked, ReadUntracked, Set, Track, Trigger},
traits::{Get, GetUntracked, Notify, ReadUntracked, Set, Track},
wrappers::write::SignalSetter,
};
use send_wrapper::SendWrapper;
@ -124,7 +124,7 @@ where
ScopedFuture::new(async move {
let triggers = join_all(loaders).await;
for trigger in triggers {
trigger.trigger();
trigger.notify();
}
matched_view.rebuild(&mut *view.borrow_mut());
})
@ -179,7 +179,7 @@ where
let triggers = join_all(loaders).await;
// tell each one of the outlet triggers that it's ready
for trigger in triggers {
trigger.trigger();
trigger.notify();
}
if let Some(loc) = location {
loc.ready_to_complete();