Skip to main content

reactive_graph/signal/
mapped.rs

1use super::{
2    guards::{Mapped, MappedMutArc},
3    ArcRwSignal, RwSignal,
4};
5use crate::{
6    owner::{StoredValue, SyncStorage},
7    signal::guards::WriteGuard,
8    traits::{
9        DefinedAt, GetValue, IsDisposed, Notify, ReadUntracked, Track,
10        UntrackableGuard, Write,
11    },
12};
13use guardian::ArcRwLockWriteGuardian;
14use std::{
15    fmt::Debug,
16    ops::{Deref, DerefMut},
17    panic::Location,
18    sync::Arc,
19};
20
21/// A derived signal type that wraps an [`ArcRwSignal`] with a mapping function,
22///  allowing you to read or write directly to one of its field.
23///
24/// Tracking the mapped signal tracks changes to *any* part of the signal, and updating the signal notifies
25/// and notifies *all* dependencies of the signal. This is not a mechanism for fine-grained reactive updates
26/// to more complex data structures. Instead, it allows you to provide a signal-like API for wrapped types
27/// without exposing the original type directly to users.
28pub struct ArcMappedSignal<T> {
29    #[cfg(any(debug_assertions, leptos_debuginfo))]
30    defined_at: &'static Location<'static>,
31    #[allow(clippy::type_complexity)]
32    try_read_untracked: Arc<
33        dyn Fn() -> Option<DoubleDeref<Box<dyn Deref<Target = T>>>>
34            + Send
35            + Sync,
36    >,
37    try_write: Arc<
38        dyn Fn() -> Option<Box<dyn UntrackableGuard<Target = T>>> + Send + Sync,
39    >,
40    notify: Arc<dyn Fn() + Send + Sync>,
41    track: Arc<dyn Fn() + Send + Sync>,
42}
43
44impl<T> Clone for ArcMappedSignal<T> {
45    fn clone(&self) -> Self {
46        Self {
47            #[cfg(any(debug_assertions, leptos_debuginfo))]
48            defined_at: self.defined_at,
49            try_read_untracked: self.try_read_untracked.clone(),
50            try_write: self.try_write.clone(),
51            notify: self.notify.clone(),
52            track: self.track.clone(),
53        }
54    }
55}
56
57impl<T> ArcMappedSignal<T> {
58    /// Wraps a signal with the given mapping functions for shared and exclusive references.
59    #[track_caller]
60    pub fn new<U>(
61        inner: ArcRwSignal<U>,
62        map: fn(&U) -> &T,
63        map_mut: fn(&mut U) -> &mut T,
64    ) -> Self
65    where
66        T: 'static,
67        U: Send + Sync + 'static,
68    {
69        Self {
70            #[cfg(any(debug_assertions, leptos_debuginfo))]
71            defined_at: Location::caller(),
72            try_read_untracked: {
73                let this = inner.clone();
74                Arc::new(move || {
75                    this.try_read_untracked().map(|guard| DoubleDeref {
76                        inner: Box::new(Mapped::new_with_guard(guard, map))
77                            as Box<dyn Deref<Target = T>>,
78                    })
79                })
80            },
81            try_write: {
82                let this = inner.clone();
83                Arc::new(move || {
84                    let guard = ArcRwLockWriteGuardian::try_take(Arc::clone(
85                        &this.value,
86                    ))?
87                    .ok()?;
88                    let mapped = WriteGuard::new(
89                        this.clone(),
90                        MappedMutArc::new(guard, map, map_mut),
91                    );
92                    Some(Box::new(mapped))
93                })
94            },
95            notify: {
96                let this = inner.clone();
97                Arc::new(move || {
98                    this.notify();
99                })
100            },
101            track: {
102                Arc::new(move || {
103                    inner.track();
104                })
105            },
106        }
107    }
108}
109
110impl<T> Debug for ArcMappedSignal<T> {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        let mut partial = f.debug_struct("ArcMappedSignal");
113        #[cfg(any(debug_assertions, leptos_debuginfo))]
114        partial.field("defined_at", &self.defined_at);
115        partial.finish()
116    }
117}
118
119impl<T> DefinedAt for ArcMappedSignal<T> {
120    fn defined_at(&self) -> Option<&'static Location<'static>> {
121        #[cfg(any(debug_assertions, leptos_debuginfo))]
122        {
123            Some(self.defined_at)
124        }
125        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
126        {
127            None
128        }
129    }
130}
131
132impl<T> Notify for ArcMappedSignal<T> {
133    fn notify(&self) {
134        (self.notify)()
135    }
136}
137
138impl<T> Track for ArcMappedSignal<T> {
139    fn track(&self) {
140        (self.track)()
141    }
142}
143
144impl<T> ReadUntracked for ArcMappedSignal<T> {
145    type Value = DoubleDeref<Box<dyn Deref<Target = T>>>;
146
147    fn try_read_untracked(&self) -> Option<Self::Value> {
148        (self.try_read_untracked)()
149    }
150}
151
152impl<T> IsDisposed for ArcMappedSignal<T> {
153    fn is_disposed(&self) -> bool {
154        false
155    }
156}
157
158impl<T> Write for ArcMappedSignal<T>
159where
160    T: 'static,
161{
162    type Value = T;
163
164    fn try_write_untracked(
165        &self,
166    ) -> Option<impl DerefMut<Target = Self::Value>> {
167        let mut guard = self.try_write()?;
168        guard.untrack();
169        Some(guard)
170    }
171
172    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
173        let inner = (self.try_write)()?;
174        let inner = DoubleDeref { inner };
175        Some(inner)
176    }
177}
178
179/// A wrapper for a smart pointer that implements [`Deref`] and [`DerefMut`]
180/// by dereferencing the type *inside* the smart pointer.
181///
182/// This is quite obscure and mostly useful for situations in which we want
183/// a wrapper for `Box<dyn Deref<Target = T>>` that dereferences to `T` rather
184/// than dereferencing to `dyn Deref<Target = T>`.
185///
186/// This is used internally in [`MappedSignal`] and [`ArcMappedSignal`].
187pub struct DoubleDeref<T> {
188    inner: T,
189}
190
191impl<T> Deref for DoubleDeref<T>
192where
193    T: Deref,
194    T::Target: Deref,
195{
196    type Target = <T::Target as Deref>::Target;
197
198    fn deref(&self) -> &Self::Target {
199        self.inner.deref().deref()
200    }
201}
202
203impl<T> DerefMut for DoubleDeref<T>
204where
205    T: DerefMut,
206    T::Target: DerefMut,
207{
208    fn deref_mut(&mut self) -> &mut Self::Target {
209        self.inner.deref_mut().deref_mut()
210    }
211}
212
213impl<T> UntrackableGuard for DoubleDeref<T>
214where
215    T: UntrackableGuard,
216    T::Target: DerefMut,
217{
218    fn untrack(&mut self) {
219        self.inner.untrack();
220    }
221}
222
223/// A derived signal type that wraps an [`RwSignal`] with a mapping function,
224///  allowing you to read or write directly to one of its field.
225///
226/// Tracking the mapped signal tracks changes to *any* part of the signal, and updating the signal notifies
227/// and notifies *all* dependencies of the signal. This is not a mechanism for fine-grained reactive updates
228/// to more complex data structures. Instead, it allows you to provide a signal-like API for wrapped types
229/// without exposing the original type directly to users.
230pub struct MappedSignal<T, S = SyncStorage> {
231    #[cfg(any(debug_assertions, leptos_debuginfo))]
232    defined_at: &'static Location<'static>,
233    inner: StoredValue<ArcMappedSignal<T>, S>,
234}
235
236impl<T> MappedSignal<T> {
237    /// Wraps a signal with the given mapping functions for shared and exclusive references.
238    #[track_caller]
239    pub fn new<U>(
240        inner: RwSignal<U>,
241        map: fn(&U) -> &T,
242        map_mut: fn(&mut U) -> &mut T,
243    ) -> Self
244    where
245        T: Send + Sync + 'static,
246        U: Send + Sync + 'static,
247    {
248        Self {
249            #[cfg(any(debug_assertions, leptos_debuginfo))]
250            defined_at: Location::caller(),
251            inner: {
252                let this = ArcRwSignal::from(inner);
253                StoredValue::new_with_storage(ArcMappedSignal::new(
254                    this, map, map_mut,
255                ))
256            },
257        }
258    }
259}
260
261impl<T> Copy for MappedSignal<T> {}
262
263impl<T> Clone for MappedSignal<T> {
264    fn clone(&self) -> Self {
265        *self
266    }
267}
268
269impl<T> Debug for MappedSignal<T> {
270    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271        let mut partial = f.debug_struct("MappedSignal");
272        #[cfg(any(debug_assertions, leptos_debuginfo))]
273        partial.field("defined_at", &self.defined_at);
274        partial.finish()
275    }
276}
277
278impl<T> DefinedAt for MappedSignal<T> {
279    fn defined_at(&self) -> Option<&'static Location<'static>> {
280        #[cfg(any(debug_assertions, leptos_debuginfo))]
281        {
282            Some(self.defined_at)
283        }
284        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
285        {
286            None
287        }
288    }
289}
290
291impl<T> Notify for MappedSignal<T>
292where
293    T: 'static,
294{
295    fn notify(&self) {
296        if let Some(inner) = self.inner.try_get_value() {
297            inner.notify();
298        }
299    }
300}
301
302impl<T> Track for MappedSignal<T>
303where
304    T: 'static,
305{
306    fn track(&self) {
307        if let Some(inner) = self.inner.try_get_value() {
308            inner.track();
309        }
310    }
311}
312
313impl<T> ReadUntracked for MappedSignal<T>
314where
315    T: 'static,
316{
317    type Value = DoubleDeref<Box<dyn Deref<Target = T>>>;
318
319    fn try_read_untracked(&self) -> Option<Self::Value> {
320        self.inner
321            .try_get_value()
322            .and_then(|inner| inner.try_read_untracked())
323    }
324}
325
326impl<T> Write for MappedSignal<T>
327where
328    T: 'static,
329{
330    type Value = T;
331
332    fn try_write_untracked(
333        &self,
334    ) -> Option<impl DerefMut<Target = Self::Value>> {
335        let mut guard = self.try_write()?;
336        guard.untrack();
337        Some(guard)
338    }
339
340    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
341        let inner = self.inner.try_get_value()?;
342        let inner = (inner.try_write)()?;
343        let inner = DoubleDeref { inner };
344        Some(inner)
345    }
346}
347
348impl<T> From<ArcMappedSignal<T>> for MappedSignal<T>
349where
350    T: 'static,
351{
352    #[track_caller]
353    fn from(value: ArcMappedSignal<T>) -> Self {
354        MappedSignal {
355            #[cfg(any(debug_assertions, leptos_debuginfo))]
356            defined_at: Location::caller(),
357            inner: StoredValue::new(value),
358        }
359    }
360}
361
362impl<T> IsDisposed for MappedSignal<T> {
363    fn is_disposed(&self) -> bool {
364        self.inner.is_disposed()
365    }
366}