reactive_graph/signal/
read.rs

1use super::{
2    guards::{Plain, ReadGuard},
3    subscriber_traits::AsSubscriberSet,
4    ArcReadSignal,
5};
6use crate::{
7    graph::SubscriberSet,
8    owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},
9    traits::{DefinedAt, Dispose, IntoInner, IsDisposed, ReadUntracked},
10    unwrap_signal,
11};
12use core::fmt::Debug;
13use std::{
14    hash::Hash,
15    panic::Location,
16    sync::{Arc, RwLock},
17};
18
19/// An arena-allocated getter for a reactive signal.
20///
21/// A signal is a piece of data that may change over time,
22/// and notifies other code when it has changed.
23///
24/// This is an arena-allocated signal, which is `Copy` and is disposed when its reactive
25/// [`Owner`](crate::owner::Owner) cleans up. For a reference-counted signal that lives
26/// as long as a reference to it is alive, see [`ArcReadSignal`].
27///
28/// ## Core Trait Implementations
29/// - [`.get()`](crate::traits::Get) clones the current value of the signal.
30///   If you call it within an effect, it will cause that effect to subscribe
31///   to the signal, and to re-run whenever the value of the signal changes.
32///   - [`.get_untracked()`](crate::traits::GetUntracked) clones the value of
33///     the signal without reactively tracking it.
34/// - [`.read()`](crate::traits::Read) returns a guard that allows accessing the
35///   value of the signal by reference. If you call it within an effect, it will
36///   cause that effect to subscribe to the signal, and to re-run whenever the
37///   value of the signal changes.
38///   - [`.read_untracked()`](crate::traits::ReadUntracked) gives access to the
39///     current value of the signal without reactively tracking it.
40/// - [`.with()`](crate::traits::With) allows you to reactively access the signal’s
41///   value without cloning by applying a callback function.
42///   - [`.with_untracked()`](crate::traits::WithUntracked) allows you to access
43///     the signal’s value by applying a callback function without reactively
44///     tracking it.
45/// - [`.to_stream()`](crate::traits::ToStream) converts the signal to an `async`
46///   stream of values.
47/// - [`::from_stream()`](crate::traits::FromStream) converts an `async` stream
48///   of values into a signal containing the latest value.
49///
50/// ## Examples
51/// ```
52/// # use reactive_graph::prelude::*; use reactive_graph::signal::*;  let owner = reactive_graph::owner::Owner::new(); owner.set();
53/// let (count, set_count) = signal(0);
54///
55/// // calling .get() clones and returns the value
56/// assert_eq!(count.get(), 0);
57/// // calling .read() accesses the value by reference
58/// assert_eq!(count.read(), 0);
59/// ```
60pub struct ReadSignal<T, S = SyncStorage> {
61    #[cfg(any(debug_assertions, leptos_debuginfo))]
62    pub(crate) defined_at: &'static Location<'static>,
63    pub(crate) inner: ArenaItem<ArcReadSignal<T>, S>,
64}
65
66impl<T, S> Dispose for ReadSignal<T, S> {
67    fn dispose(self) {
68        self.inner.dispose()
69    }
70}
71
72impl<T, S> Copy for ReadSignal<T, S> {}
73
74impl<T, S> Clone for ReadSignal<T, S> {
75    fn clone(&self) -> Self {
76        *self
77    }
78}
79
80impl<T, S> Debug for ReadSignal<T, S>
81where
82    S: Debug,
83{
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        f.debug_struct("ReadSignal")
86            .field("type", &std::any::type_name::<T>())
87            .field("store", &self.inner)
88            .finish()
89    }
90}
91
92impl<T, S> PartialEq for ReadSignal<T, S> {
93    fn eq(&self, other: &Self) -> bool {
94        self.inner == other.inner
95    }
96}
97
98impl<T, S> Eq for ReadSignal<T, S> {}
99
100impl<T, S> Hash for ReadSignal<T, S> {
101    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
102        self.inner.hash(state);
103    }
104}
105
106impl<T, S> DefinedAt for ReadSignal<T, S> {
107    fn defined_at(&self) -> Option<&'static Location<'static>> {
108        #[cfg(any(debug_assertions, leptos_debuginfo))]
109        {
110            Some(self.defined_at)
111        }
112        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
113        {
114            None
115        }
116    }
117}
118
119impl<T, S> IsDisposed for ReadSignal<T, S> {
120    fn is_disposed(&self) -> bool {
121        self.inner.is_disposed()
122    }
123}
124
125impl<T, S> IntoInner for ReadSignal<T, S>
126where
127    S: Storage<ArcReadSignal<T>>,
128{
129    type Value = T;
130
131    #[inline(always)]
132    fn into_inner(self) -> Option<Self::Value> {
133        self.inner.into_inner()?.into_inner()
134    }
135}
136
137impl<T, S> AsSubscriberSet for ReadSignal<T, S>
138where
139    S: Storage<ArcReadSignal<T>>,
140{
141    type Output = Arc<RwLock<SubscriberSet>>;
142
143    fn as_subscriber_set(&self) -> Option<Self::Output> {
144        self.inner
145            .try_with_value(|inner| inner.as_subscriber_set())
146            .flatten()
147    }
148}
149
150impl<T, S> ReadUntracked for ReadSignal<T, S>
151where
152    T: 'static,
153    S: Storage<ArcReadSignal<T>>,
154{
155    type Value = ReadGuard<T, Plain<T>>;
156
157    fn try_read_untracked(&self) -> Option<Self::Value> {
158        self.inner
159            .try_get_value()
160            .map(|inner| inner.read_untracked())
161    }
162}
163
164impl<T> From<ArcReadSignal<T>> for ReadSignal<T>
165where
166    T: Send + Sync + 'static,
167{
168    #[track_caller]
169    fn from(value: ArcReadSignal<T>) -> Self {
170        ReadSignal {
171            #[cfg(any(debug_assertions, leptos_debuginfo))]
172            defined_at: Location::caller(),
173            inner: ArenaItem::new_with_storage(value),
174        }
175    }
176}
177
178impl<T> FromLocal<ArcReadSignal<T>> for ReadSignal<T, LocalStorage>
179where
180    T: 'static,
181{
182    #[track_caller]
183    fn from_local(value: ArcReadSignal<T>) -> Self {
184        ReadSignal {
185            #[cfg(any(debug_assertions, leptos_debuginfo))]
186            defined_at: Location::caller(),
187            inner: ArenaItem::new_with_storage(value),
188        }
189    }
190}
191
192impl<T, S> From<ReadSignal<T, S>> for ArcReadSignal<T>
193where
194    T: 'static,
195    S: Storage<ArcReadSignal<T>>,
196{
197    #[track_caller]
198    fn from(value: ReadSignal<T, S>) -> Self {
199        value
200            .inner
201            .try_get_value()
202            .unwrap_or_else(unwrap_signal!(value))
203    }
204}