reactive_graph/owner/
arc_stored_value.rs

1use crate::{
2    signal::guards::{Plain, ReadGuard, UntrackedWriteGuard},
3    traits::{DefinedAt, IntoInner, IsDisposed, ReadValue, WriteValue},
4};
5use std::{
6    fmt::{Debug, Formatter},
7    hash::Hash,
8    panic::Location,
9    sync::{Arc, RwLock},
10};
11
12/// A reference-counted getter for any value non-reactively.
13///
14/// This is a reference-counted value, which is `Clone` but not `Copy`.
15/// For arena-allocated `Copy` values, use [`StoredValue`](super::StoredValue).
16///
17/// This allows you to create a stable reference for any value by storing it within
18/// the reactive system. Unlike e.g. [`ArcRwSignal`](crate::signal::ArcRwSignal), it is not reactive;
19/// accessing it does not cause effects to subscribe, and
20/// updating it does not notify anything else.
21pub struct ArcStoredValue<T> {
22    #[cfg(any(debug_assertions, leptos_debuginfo))]
23    defined_at: &'static Location<'static>,
24    value: Arc<RwLock<T>>,
25}
26
27impl<T> Clone for ArcStoredValue<T> {
28    fn clone(&self) -> Self {
29        Self {
30            #[cfg(any(debug_assertions, leptos_debuginfo))]
31            defined_at: self.defined_at,
32            value: Arc::clone(&self.value),
33        }
34    }
35}
36
37impl<T> Debug for ArcStoredValue<T> {
38    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
39        f.debug_struct("ArcStoredValue")
40            .field("type", &std::any::type_name::<T>())
41            .field("value", &Arc::as_ptr(&self.value))
42            .finish()
43    }
44}
45
46impl<T: Default> Default for ArcStoredValue<T> {
47    #[track_caller]
48    fn default() -> Self {
49        Self {
50            #[cfg(any(debug_assertions, leptos_debuginfo))]
51            defined_at: Location::caller(),
52            value: Arc::new(RwLock::new(T::default())),
53        }
54    }
55}
56
57impl<T> PartialEq for ArcStoredValue<T> {
58    fn eq(&self, other: &Self) -> bool {
59        Arc::ptr_eq(&self.value, &other.value)
60    }
61}
62
63impl<T> Eq for ArcStoredValue<T> {}
64
65impl<T> Hash for ArcStoredValue<T> {
66    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
67        std::ptr::hash(&Arc::as_ptr(&self.value), state);
68    }
69}
70
71impl<T> DefinedAt for ArcStoredValue<T> {
72    fn defined_at(&self) -> Option<&'static Location<'static>> {
73        #[cfg(any(debug_assertions, leptos_debuginfo))]
74        {
75            Some(self.defined_at)
76        }
77        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
78        {
79            None
80        }
81    }
82}
83
84impl<T> ArcStoredValue<T> {
85    /// Creates a new stored value, taking the initial value as its argument.
86    #[cfg_attr(
87        feature = "tracing",
88        tracing::instrument(level = "trace", skip_all)
89    )]
90    #[track_caller]
91    pub fn new(value: T) -> Self {
92        Self {
93            #[cfg(any(debug_assertions, leptos_debuginfo))]
94            defined_at: Location::caller(),
95            value: Arc::new(RwLock::new(value)),
96        }
97    }
98}
99
100impl<T> ReadValue for ArcStoredValue<T>
101where
102    T: 'static,
103{
104    type Value = ReadGuard<T, Plain<T>>;
105
106    fn try_read_value(&self) -> Option<ReadGuard<T, Plain<T>>> {
107        Plain::try_new(Arc::clone(&self.value)).map(ReadGuard::new)
108    }
109}
110
111impl<T> WriteValue for ArcStoredValue<T>
112where
113    T: 'static,
114{
115    type Value = T;
116
117    fn try_write_value(&self) -> Option<UntrackedWriteGuard<T>> {
118        UntrackedWriteGuard::try_new(self.value.clone())
119    }
120}
121
122impl<T> IsDisposed for ArcStoredValue<T> {
123    fn is_disposed(&self) -> bool {
124        false
125    }
126}
127
128impl<T> IntoInner for ArcStoredValue<T> {
129    type Value = T;
130
131    #[inline(always)]
132    fn into_inner(self) -> Option<Self::Value> {
133        Some(Arc::into_inner(self.value)?.into_inner().unwrap())
134    }
135}