reactive_stores/
iter.rs

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