reactive_graph/owner/
stored_value.rs

1use super::{
2    arc_stored_value::ArcStoredValue, ArenaItem, LocalStorage, Storage,
3    SyncStorage,
4};
5use crate::{
6    signal::guards::{Plain, ReadGuard, UntrackedWriteGuard},
7    traits::{
8        DefinedAt, Dispose, IntoInner, IsDisposed, ReadValue, WriteValue,
9    },
10    unwrap_signal,
11};
12use std::{
13    fmt::{Debug, Formatter},
14    hash::Hash,
15    panic::Location,
16};
17
18/// A **non-reactive**, `Copy` handle for any value.
19///
20/// This allows you to create a stable reference for any value by storing it within
21/// the reactive system. Like the signal types (e.g., [`ReadSignal`](crate::signal::ReadSignal)
22/// and [`RwSignal`](crate::signal::RwSignal)), it is `Copy` and `'static`. Unlike the signal
23/// types, it is not reactive; accessing it does not cause effects to subscribe, and
24/// updating it does not notify anything else.
25pub struct StoredValue<T, S = SyncStorage> {
26    value: ArenaItem<ArcStoredValue<T>, S>,
27    #[cfg(any(debug_assertions, leptos_debuginfo))]
28    defined_at: &'static Location<'static>,
29}
30
31impl<T, S> Copy for StoredValue<T, S> {}
32
33impl<T, S> Clone for StoredValue<T, S> {
34    fn clone(&self) -> Self {
35        *self
36    }
37}
38
39impl<T, S> Debug for StoredValue<T, S>
40where
41    S: Debug,
42{
43    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
44        f.debug_struct("StoredValue")
45            .field("type", &std::any::type_name::<T>())
46            .field("value", &self.value)
47            .finish()
48    }
49}
50
51impl<T, S> PartialEq for StoredValue<T, S> {
52    fn eq(&self, other: &Self) -> bool {
53        self.value == other.value
54    }
55}
56
57impl<T, S> Eq for StoredValue<T, S> {}
58
59impl<T, S> Hash for StoredValue<T, S> {
60    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
61        self.value.hash(state);
62    }
63}
64
65impl<T, S> DefinedAt for StoredValue<T, S> {
66    fn defined_at(&self) -> Option<&'static Location<'static>> {
67        #[cfg(any(debug_assertions, leptos_debuginfo))]
68        {
69            Some(self.defined_at)
70        }
71        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
72        {
73            None
74        }
75    }
76}
77
78impl<T, S> StoredValue<T, S>
79where
80    T: 'static,
81    S: Storage<ArcStoredValue<T>>,
82{
83    /// Stores the given value in the arena allocator.
84    #[track_caller]
85    pub fn new_with_storage(value: T) -> Self {
86        Self {
87            value: ArenaItem::new_with_storage(ArcStoredValue::new(value)),
88            #[cfg(any(debug_assertions, leptos_debuginfo))]
89            defined_at: Location::caller(),
90        }
91    }
92}
93
94impl<T, S> Default for StoredValue<T, S>
95where
96    T: Default + 'static,
97    S: Storage<ArcStoredValue<T>>,
98{
99    #[track_caller] // Default trait is not annotated with #[track_caller]
100    fn default() -> Self {
101        Self::new_with_storage(Default::default())
102    }
103}
104
105impl<T> StoredValue<T>
106where
107    T: Send + Sync + 'static,
108{
109    /// Stores the given value in the arena allocator.
110    #[track_caller]
111    pub fn new(value: T) -> Self {
112        StoredValue::new_with_storage(value)
113    }
114}
115
116impl<T> StoredValue<T, LocalStorage>
117where
118    T: 'static,
119{
120    /// Stores the given value in the arena allocator.
121    #[track_caller]
122    pub fn new_local(value: T) -> Self {
123        StoredValue::new_with_storage(value)
124    }
125}
126
127impl<T, S> ReadValue for StoredValue<T, S>
128where
129    T: 'static,
130    S: Storage<ArcStoredValue<T>>,
131{
132    type Value = ReadGuard<T, Plain<T>>;
133
134    fn try_read_value(&self) -> Option<ReadGuard<T, Plain<T>>> {
135        self.value
136            .try_get_value()
137            .and_then(|inner| inner.try_read_value())
138    }
139}
140
141impl<T, S> WriteValue for StoredValue<T, S>
142where
143    T: 'static,
144    S: Storage<ArcStoredValue<T>>,
145{
146    type Value = T;
147
148    fn try_write_value(&self) -> Option<UntrackedWriteGuard<T>> {
149        self.value
150            .try_get_value()
151            .and_then(|inner| inner.try_write_value())
152    }
153}
154
155impl<T, S> IsDisposed for StoredValue<T, S> {
156    fn is_disposed(&self) -> bool {
157        self.value.is_disposed()
158    }
159}
160
161impl<T, S> Dispose for StoredValue<T, S> {
162    fn dispose(self) {
163        self.value.dispose();
164    }
165}
166
167impl<T, S> IntoInner for StoredValue<T, S>
168where
169    T: 'static,
170    S: Storage<ArcStoredValue<T>>,
171{
172    type Value = T;
173
174    #[inline(always)]
175    fn into_inner(self) -> Option<Self::Value> {
176        self.value.into_inner()?.into_inner()
177    }
178}
179
180impl<T> From<ArcStoredValue<T>> for StoredValue<T>
181where
182    T: Send + Sync + 'static,
183{
184    #[track_caller]
185    fn from(value: ArcStoredValue<T>) -> Self {
186        StoredValue {
187            #[cfg(any(debug_assertions, leptos_debuginfo))]
188            defined_at: Location::caller(),
189            value: ArenaItem::new(value),
190        }
191    }
192}
193
194impl<T, S> From<StoredValue<T, S>> for ArcStoredValue<T>
195where
196    S: Storage<ArcStoredValue<T>>,
197{
198    #[track_caller]
199    fn from(value: StoredValue<T, S>) -> Self {
200        value
201            .value
202            .try_get_value()
203            .unwrap_or_else(unwrap_signal!(value))
204    }
205}
206
207/// Creates a new [`StoredValue`].
208#[inline(always)]
209#[track_caller]
210#[deprecated(
211    since = "0.7.0-beta5",
212    note = "This function is being removed to conform to Rust idioms. Please \
213            use `StoredValue::new()` or `StoredValue::new_local()` instead."
214)]
215pub fn store_value<T>(value: T) -> StoredValue<T>
216where
217    T: Send + Sync + 'static,
218{
219    StoredValue::new(value)
220}
221
222/// Converts some value into a locally-stored type, using [`LocalStorage`].
223///
224/// This is modeled on [`From`] but special-cased for this thread-local storage method, which
225/// allows for better type inference for the default case.
226pub trait FromLocal<T> {
227    /// Converts between the types.
228    fn from_local(value: T) -> Self;
229}