reactive_graph/signal/
arc_read.rs

1use super::{
2    guards::{Plain, ReadGuard},
3    subscriber_traits::AsSubscriberSet,
4};
5use crate::{
6    graph::SubscriberSet,
7    traits::{DefinedAt, IntoInner, IsDisposed, ReadUntracked},
8};
9use core::fmt::{Debug, Formatter, Result};
10use std::{
11    hash::Hash,
12    panic::Location,
13    sync::{Arc, RwLock},
14};
15
16/// A reference-counted getter for a reactive signal.
17///
18/// A signal is a piece of data that may change over time,
19/// and notifies other code when it has changed.
20///
21/// This is a reference-counted signal, which is `Clone` but not `Copy`.
22/// For arena-allocated `Copy` signals, use [`ReadSignal`](super::ReadSignal).
23///
24/// ## Core Trait Implementations
25/// - [`.get()`](crate::traits::Get) clones the current value of the signal.
26///   If you call it within an effect, it will cause that effect to subscribe
27///   to the signal, and to re-run whenever the value of the signal changes.
28///   - [`.get_untracked()`](crate::traits::GetUntracked) clones the value of
29///     the signal without reactively tracking it.
30/// - [`.read()`](crate::traits::Read) returns a guard that allows accessing the
31///   value of the signal by reference. If you call it within an effect, it will
32///   cause that effect to subscribe to the signal, and to re-run whenever the
33///   value of the signal changes.
34///   - [`.read_untracked()`](crate::traits::ReadUntracked) gives access to the
35///     current value of the signal without reactively tracking it.
36/// - [`.with()`](crate::traits::With) allows you to reactively access the signal’s
37///   value without cloning by applying a callback function.
38///   - [`.with_untracked()`](crate::traits::WithUntracked) allows you to access
39///     the signal’s value by applying a callback function without reactively
40///     tracking it.
41/// - [`.to_stream()`](crate::traits::ToStream) converts the signal to an `async`
42///   stream of values.
43/// - [`::from_stream()`](crate::traits::FromStream) converts an `async` stream
44///   of values into a signal containing the latest value.
45///
46/// ## Examples
47/// ```
48/// # use reactive_graph::prelude::*; use reactive_graph::signal::*;
49/// let (count, set_count) = arc_signal(0);
50///
51/// // calling .get() clones and returns the value
52/// assert_eq!(count.get(), 0);
53/// // calling .read() accesses the value by reference
54/// assert_eq!(count.read(), 0);
55/// ```
56pub struct ArcReadSignal<T> {
57    #[cfg(any(debug_assertions, leptos_debuginfo))]
58    pub(crate) defined_at: &'static Location<'static>,
59    pub(crate) value: Arc<RwLock<T>>,
60    pub(crate) inner: Arc<RwLock<SubscriberSet>>,
61}
62
63impl<T> Clone for ArcReadSignal<T> {
64    #[track_caller]
65    fn clone(&self) -> Self {
66        Self {
67            #[cfg(any(debug_assertions, leptos_debuginfo))]
68            defined_at: self.defined_at,
69            value: Arc::clone(&self.value),
70            inner: Arc::clone(&self.inner),
71        }
72    }
73}
74
75impl<T> Debug for ArcReadSignal<T> {
76    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
77        f.debug_struct("ArcReadSignal")
78            .field("type", &std::any::type_name::<T>())
79            .field("value", &Arc::as_ptr(&self.value))
80            .finish()
81    }
82}
83
84impl<T: Default> Default for ArcReadSignal<T> {
85    #[track_caller]
86    fn default() -> Self {
87        Self {
88            #[cfg(any(debug_assertions, leptos_debuginfo))]
89            defined_at: Location::caller(),
90            value: Arc::new(RwLock::new(T::default())),
91            inner: Arc::new(RwLock::new(SubscriberSet::new())),
92        }
93    }
94}
95
96impl<T> PartialEq for ArcReadSignal<T> {
97    fn eq(&self, other: &Self) -> bool {
98        Arc::ptr_eq(&self.value, &other.value)
99    }
100}
101
102impl<T> Eq for ArcReadSignal<T> {}
103
104impl<T> Hash for ArcReadSignal<T> {
105    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
106        std::ptr::hash(&Arc::as_ptr(&self.value), state);
107    }
108}
109
110impl<T> DefinedAt for ArcReadSignal<T> {
111    #[inline(always)]
112    fn defined_at(&self) -> Option<&'static Location<'static>> {
113        #[cfg(any(debug_assertions, leptos_debuginfo))]
114        {
115            Some(self.defined_at)
116        }
117        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
118        {
119            None
120        }
121    }
122}
123
124impl<T> IsDisposed for ArcReadSignal<T> {
125    #[inline(always)]
126    fn is_disposed(&self) -> bool {
127        false
128    }
129}
130
131impl<T> IntoInner for ArcReadSignal<T> {
132    type Value = T;
133
134    #[inline(always)]
135    fn into_inner(self) -> Option<Self::Value> {
136        Some(Arc::into_inner(self.value)?.into_inner().unwrap())
137    }
138}
139
140impl<T> AsSubscriberSet for ArcReadSignal<T> {
141    type Output = Arc<RwLock<SubscriberSet>>;
142
143    #[inline(always)]
144    fn as_subscriber_set(&self) -> Option<Self::Output> {
145        Some(Arc::clone(&self.inner))
146    }
147}
148
149impl<T: 'static> ReadUntracked for ArcReadSignal<T> {
150    type Value = ReadGuard<T, Plain<T>>;
151
152    #[track_caller]
153    fn try_read_untracked(&self) -> Option<Self::Value> {
154        Plain::try_new(Arc::clone(&self.value)).map(ReadGuard::new)
155    }
156}