Skip to main content

reactive_stores/
subfield.rs

1use crate::{
2    path::{StorePath, StorePathSegment},
3    store_field::StoreField,
4    KeyMap, StoreFieldTrigger,
5};
6use reactive_graph::{
7    signal::{
8        guards::{Mapped, MappedMut, WriteGuard},
9        ArcTrigger,
10    },
11    traits::{
12        DefinedAt, Get as _, IsDisposed, Notify, ReadUntracked, Track,
13        UntrackableGuard, Write,
14    },
15    wrappers::read::Signal,
16};
17use std::{iter, marker::PhantomData, ops::DerefMut, panic::Location};
18
19/// Accesses a single field of a reactive structure.
20#[derive(Debug)]
21pub struct Subfield<Inner, Prev, T> {
22    #[cfg(any(debug_assertions, leptos_debuginfo))]
23    defined_at: &'static Location<'static>,
24    path_segment: StorePathSegment,
25    inner: Inner,
26    read: fn(&Prev) -> &T,
27    write: fn(&mut Prev) -> &mut T,
28    ty: PhantomData<T>,
29}
30
31impl<Inner, Prev, T> Clone for Subfield<Inner, Prev, T>
32where
33    Inner: Clone,
34{
35    fn clone(&self) -> Self {
36        Self {
37            #[cfg(any(debug_assertions, leptos_debuginfo))]
38            defined_at: self.defined_at,
39            path_segment: self.path_segment,
40            inner: self.inner.clone(),
41            read: self.read,
42            write: self.write,
43            ty: self.ty,
44        }
45    }
46}
47
48impl<Inner, Prev, T> Copy for Subfield<Inner, Prev, T> where Inner: Copy {}
49
50impl<Inner, Prev, T> Subfield<Inner, Prev, T> {
51    /// Creates an accessor for a single field of the inner structure.
52    #[track_caller]
53    pub fn new(
54        inner: Inner,
55        path_segment: StorePathSegment,
56        read: fn(&Prev) -> &T,
57        write: fn(&mut Prev) -> &mut T,
58    ) -> Self {
59        Self {
60            #[cfg(any(debug_assertions, leptos_debuginfo))]
61            defined_at: Location::caller(),
62            inner,
63            path_segment,
64            read,
65            write,
66            ty: PhantomData,
67        }
68    }
69}
70
71impl<Inner, Prev, T> StoreField for Subfield<Inner, Prev, T>
72where
73    Inner: StoreField<Value = Prev>,
74    Prev: 'static,
75{
76    type Value = T;
77    type Reader = Mapped<Inner::Reader, T>;
78    type Writer = MappedMut<WriteGuard<Vec<ArcTrigger>, Inner::Writer>, T>;
79
80    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
81        self.inner
82            .path()
83            .into_iter()
84            .chain(iter::once(self.path_segment))
85    }
86
87    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {
88        self.inner
89            .path_unkeyed()
90            .into_iter()
91            .chain(iter::once(self.path_segment))
92    }
93
94    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {
95        self.inner.get_trigger(path)
96    }
97
98    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {
99        self.inner.get_trigger_unkeyed(path)
100    }
101
102    fn reader(&self) -> Option<Self::Reader> {
103        let inner = self.inner.reader()?;
104        Some(Mapped::new_with_guard(inner, self.read))
105    }
106
107    fn writer(&self) -> Option<Self::Writer> {
108        let mut parent = self.inner.writer()?;
109
110        // we will manually include all the parent and ancestor `children` triggers
111        // in triggers_for_current_path() below. we want to untrack the parent writer
112        // so that it doesn't notify on the parent's `this` trigger, which would notify our
113        // siblings too
114        parent.untrack();
115        let triggers = self.triggers_for_current_path();
116        let guard = WriteGuard::new(triggers, parent);
117        Some(MappedMut::new(guard, self.read, self.write))
118    }
119
120    #[inline(always)]
121    fn keys(&self) -> Option<KeyMap> {
122        self.inner.keys()
123    }
124
125    #[track_caller]
126    fn track_field(&self) {
127        let mut full_path = self.path().into_iter().collect::<StorePath>();
128        let trigger = self.get_trigger(self.path().into_iter().collect());
129        trigger.this.track();
130        trigger.children.track();
131
132        // tracks `this` for all ancestors: i.e., it will track any change that is made
133        // directly to one of its ancestors, but not a change made to a *child* of an ancestor
134        // (which would end up with every subfield tracking its own siblings, because they are
135        // children of its parent)
136        while !full_path.is_empty() {
137            full_path.pop();
138            let inner = self.get_trigger(full_path.clone());
139            inner.this.track();
140        }
141    }
142}
143
144impl<Inner, Prev, T> DefinedAt for Subfield<Inner, Prev, T>
145where
146    Inner: StoreField<Value = Prev>,
147{
148    fn defined_at(&self) -> Option<&'static Location<'static>> {
149        #[cfg(any(debug_assertions, leptos_debuginfo))]
150        {
151            Some(self.defined_at)
152        }
153        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
154        {
155            None
156        }
157    }
158}
159
160impl<Inner, Prev, T> IsDisposed for Subfield<Inner, Prev, T>
161where
162    Inner: IsDisposed,
163{
164    fn is_disposed(&self) -> bool {
165        self.inner.is_disposed()
166    }
167}
168
169impl<Inner, Prev, T> Notify for Subfield<Inner, Prev, T>
170where
171    Inner: StoreField<Value = Prev>,
172    Prev: 'static,
173{
174    #[track_caller]
175    fn notify(&self) {
176        let trigger = self.get_trigger(self.path().into_iter().collect());
177        trigger.this.notify();
178        trigger.children.notify();
179    }
180}
181
182impl<Inner, Prev, T> Track for Subfield<Inner, Prev, T>
183where
184    Inner: StoreField<Value = Prev> + Track + 'static,
185    Prev: 'static,
186    T: 'static,
187{
188    #[track_caller]
189    fn track(&self) {
190        self.track_field();
191    }
192}
193
194impl<Inner, Prev, T> ReadUntracked for Subfield<Inner, Prev, T>
195where
196    Inner: StoreField<Value = Prev>,
197    Prev: 'static,
198{
199    type Value = <Self as StoreField>::Reader;
200
201    fn try_read_untracked(&self) -> Option<Self::Value> {
202        self.reader()
203    }
204}
205
206impl<Inner, Prev, T> Write for Subfield<Inner, Prev, T>
207where
208    T: 'static,
209    Inner: StoreField<Value = Prev>,
210    Prev: 'static,
211{
212    type Value = T;
213
214    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
215        self.writer()
216    }
217
218    fn try_write_untracked(
219        &self,
220    ) -> Option<impl DerefMut<Target = Self::Value>> {
221        self.writer().map(|mut writer| {
222            writer.untrack();
223            writer
224        })
225    }
226}
227
228impl<Inner, Prev, T> From<Subfield<Inner, Prev, T>> for Signal<T>
229where
230    Inner: StoreField<Value = Prev> + Track + Send + Sync + 'static,
231    Prev: 'static,
232    T: Send + Sync + Clone + 'static,
233{
234    fn from(subfield: Subfield<Inner, Prev, T>) -> Self {
235        Signal::derive(move || subfield.get())
236    }
237}