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#[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 #[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
196pub trait StoreFieldIterator<Prev>
198where
199 Self: StoreField<Value = Prev>,
200{
201 fn at_unkeyed(self, index: usize) -> AtIndex<Self, Prev>;
203
204 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 let trigger = self.get_trigger(self.path().into_iter().collect());
223 trigger.this.track();
224 trigger.children.track();
225
226 let len = self.reader().map(|n| n.as_ref().len()).unwrap_or(0);
228
229 StoreFieldIter {
231 inner: self,
232 idx: 0,
233 len,
234 prev: PhantomData,
235 }
236 }
237}
238
239pub 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}