Skip to main content

reactive_stores/
iter.rs

1use crate::{
2    len::Len,
3    path::{StorePath, StorePathSegment},
4    store_field::StoreField,
5    KeyMap, StoreFieldTrigger,
6};
7use reactive_graph::{
8    signal::{
9        guards::{MappedMutArc, WriteGuard},
10        ArcTrigger,
11    },
12    traits::{
13        DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,
14        Write,
15    },
16};
17use std::{
18    iter,
19    marker::PhantomData,
20    ops::{DerefMut, IndexMut},
21    panic::Location,
22};
23
24/// Provides access to the data at some index in another collection.
25#[derive(Debug)]
26pub struct AtIndex<Inner, Prev> {
27    #[cfg(any(debug_assertions, leptos_debuginfo))]
28    defined_at: &'static Location<'static>,
29    inner: Inner,
30    index: usize,
31    ty: PhantomData<Prev>,
32}
33
34impl<Inner, Prev> Clone for AtIndex<Inner, Prev>
35where
36    Inner: Clone,
37{
38    fn clone(&self) -> Self {
39        Self {
40            #[cfg(any(debug_assertions, leptos_debuginfo))]
41            defined_at: self.defined_at,
42            inner: self.inner.clone(),
43            index: self.index,
44            ty: self.ty,
45        }
46    }
47}
48
49impl<Inner, Prev> Copy for AtIndex<Inner, Prev> where Inner: Copy {}
50
51impl<Inner, Prev> AtIndex<Inner, Prev> {
52    /// Creates a new accessor for the inner collection at the given index.
53    #[track_caller]
54    pub fn new(inner: Inner, index: usize) -> Self {
55        Self {
56            #[cfg(any(debug_assertions, leptos_debuginfo))]
57            defined_at: Location::caller(),
58            inner,
59            index,
60            ty: PhantomData,
61        }
62    }
63}
64
65impl<Inner, Prev> StoreField for AtIndex<Inner, Prev>
66where
67    Inner: StoreField<Value = Prev>,
68    Prev: IndexMut<usize> + 'static,
69    Prev::Output: Sized,
70{
71    type Value = Prev::Output;
72    type Reader = MappedMutArc<Inner::Reader, Prev::Output>;
73    type Writer =
74        MappedMutArc<WriteGuard<ArcTrigger, Inner::Writer>, Prev::Output>;
75
76    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
77        self.inner
78            .path()
79            .into_iter()
80            .chain(iter::once(self.index.into()))
81    }
82
83    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {
84        self.inner
85            .path_unkeyed()
86            .into_iter()
87            .chain(iter::once(self.index.into()))
88    }
89
90    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {
91        self.inner.get_trigger(path)
92    }
93
94    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {
95        self.inner.get_trigger_unkeyed(path)
96    }
97
98    fn reader(&self) -> Option<Self::Reader> {
99        let inner = self.inner.reader()?;
100        let index = self.index;
101        Some(MappedMutArc::new(
102            inner,
103            move |n| &n[index],
104            move |n| &mut n[index],
105        ))
106    }
107
108    fn writer(&self) -> Option<Self::Writer> {
109        let trigger = self.get_trigger(self.path().into_iter().collect());
110        let inner = WriteGuard::new(trigger.children, self.inner.writer()?);
111        let index = self.index;
112        Some(MappedMutArc::new(
113            inner,
114            move |n| &n[index],
115            move |n| &mut n[index],
116        ))
117    }
118
119    #[inline(always)]
120    fn keys(&self) -> Option<KeyMap> {
121        self.inner.keys()
122    }
123
124    fn track_field(&self) {
125        let mut full_path = self.path().into_iter().collect::<StorePath>();
126        let trigger = self.get_trigger(self.path().into_iter().collect());
127        trigger.this.track();
128        trigger.children.track();
129
130        // tracks `this` for all ancestors: i.e., it will track any change that is made
131        // directly to one of its ancestors, but not a change made to a *child* of an ancestor
132        // (which would end up with every subfield tracking its own siblings, because they are
133        // children of its parent)
134        while !full_path.is_empty() {
135            full_path.pop();
136            let inner = self.get_trigger(full_path.clone());
137            inner.this.track();
138        }
139    }
140}
141
142impl<Inner, Prev> DefinedAt for AtIndex<Inner, Prev>
143where
144    Inner: StoreField<Value = Prev>,
145{
146    fn defined_at(&self) -> Option<&'static Location<'static>> {
147        #[cfg(any(debug_assertions, leptos_debuginfo))]
148        {
149            Some(self.defined_at)
150        }
151        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
152        {
153            None
154        }
155    }
156}
157
158impl<Inner, Prev> IsDisposed for AtIndex<Inner, Prev>
159where
160    Inner: StoreField<Value = Prev> + IsDisposed,
161{
162    fn is_disposed(&self) -> bool {
163        self.inner.is_disposed()
164    }
165}
166
167impl<Inner, Prev> Notify for AtIndex<Inner, Prev>
168where
169    Inner: StoreField<Value = Prev>,
170    Prev: IndexMut<usize> + 'static,
171    Prev::Output: Sized,
172{
173    fn notify(&self) {
174        let trigger = self.get_trigger(self.path().into_iter().collect());
175        trigger.this.notify();
176    }
177}
178
179impl<Inner, Prev> Track for AtIndex<Inner, Prev>
180where
181    Inner: StoreField<Value = Prev> + Send + Sync + Clone + 'static,
182    Prev: IndexMut<usize> + 'static,
183    Prev::Output: Sized + 'static,
184{
185    fn track(&self) {
186        self.track_field();
187    }
188}
189
190impl<Inner, Prev> ReadUntracked for AtIndex<Inner, Prev>
191where
192    Inner: StoreField<Value = Prev>,
193    Prev: IndexMut<usize> + 'static,
194    Prev::Output: Sized,
195{
196    type Value = <Self as StoreField>::Reader;
197
198    fn try_read_untracked(&self) -> Option<Self::Value> {
199        self.reader()
200    }
201}
202
203impl<Inner, Prev> Write for AtIndex<Inner, Prev>
204where
205    Inner: StoreField<Value = Prev>,
206    Prev: IndexMut<usize> + 'static,
207    Prev::Output: Sized + 'static,
208{
209    type Value = Prev::Output;
210
211    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
212        self.writer()
213    }
214
215    fn try_write_untracked(
216        &self,
217    ) -> Option<impl DerefMut<Target = Self::Value>> {
218        self.writer().map(|mut writer| {
219            writer.untrack();
220            writer
221        })
222    }
223}
224
225/// Provides unkeyed reactive access to the fields of some collection.
226pub trait StoreFieldIterator<Prev>
227where
228    Self: StoreField<Value = Prev>,
229{
230    /// Reactive access to the value at some index.
231    fn at_unkeyed(self, index: usize) -> AtIndex<Self, Prev>;
232
233    /// An iterator over the values in the collection.
234    fn iter_unkeyed(self) -> StoreFieldIter<Self, Prev>;
235}
236
237impl<Inner, Prev> StoreFieldIterator<Prev> for Inner
238where
239    Inner: StoreField<Value = Prev> + Clone,
240    Prev::Output: Sized,
241    Prev: IndexMut<usize> + Len,
242{
243    #[track_caller]
244    fn at_unkeyed(self, index: usize) -> AtIndex<Inner, Prev> {
245        AtIndex::new(self.clone(), index)
246    }
247
248    #[track_caller]
249    fn iter_unkeyed(self) -> StoreFieldIter<Inner, Prev> {
250        // reactively track changes to this field
251        let trigger = self.get_trigger(self.path().into_iter().collect());
252        trigger.this.track();
253        trigger.children.track();
254
255        // get the current length of the field by accessing slice
256        let len = self.reader().map(|n| n.len()).unwrap_or(0);
257
258        // return the iterator
259        StoreFieldIter {
260            inner: self,
261            idx: 0,
262            len,
263            prev: PhantomData,
264        }
265    }
266}
267
268/// An iterator over the values in a collection, as reactive fields.
269pub struct StoreFieldIter<Inner, Prev> {
270    inner: Inner,
271    idx: usize,
272    len: usize,
273    prev: PhantomData<Prev>,
274}
275
276impl<Inner, Prev> Iterator for StoreFieldIter<Inner, Prev>
277where
278    Inner: StoreField<Value = Prev> + Clone + 'static,
279    Prev: IndexMut<usize> + 'static,
280    Prev::Output: Sized + 'static,
281{
282    type Item = AtIndex<Inner, Prev>;
283
284    fn next(&mut self) -> Option<Self::Item> {
285        if self.idx < self.len {
286            let field = AtIndex::new(self.inner.clone(), self.idx);
287            self.idx += 1;
288            Some(field)
289        } else {
290            None
291        }
292    }
293}
294
295impl<Inner, Prev> DoubleEndedIterator for StoreFieldIter<Inner, Prev>
296where
297    Inner: StoreField<Value = Prev> + Clone + 'static,
298    Prev: IndexMut<usize> + 'static,
299    Prev::Output: Sized + 'static,
300{
301    fn next_back(&mut self) -> Option<Self::Item> {
302        if self.len > self.idx {
303            self.len -= 1;
304            let field = AtIndex::new(self.inner.clone(), self.len);
305            Some(field)
306        } else {
307            None
308        }
309    }
310}