Skip to main content

reactive_stores/
keyed.rs

1use crate::{
2    path::{StorePath, StorePathSegment},
3    store_field::StoreField,
4    KeyMap, StoreFieldTrigger,
5};
6use reactive_graph::{
7    signal::{
8        guards::{Mapped, MappedMut, MappedMutArc, WriteGuard},
9        ArcTrigger,
10    },
11    traits::{
12        DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,
13        Write,
14    },
15};
16use std::{
17    collections::VecDeque,
18    fmt::Debug,
19    hash::Hash,
20    iter,
21    ops::{Deref, DerefMut, Index, IndexMut},
22    panic::Location,
23};
24
25/// Accesses an item from a keyed collection.
26///
27/// `K` is the identity key type used to uniquely identify entries. Collections
28/// that are indexed by position (like `Vec`) can implement this for any `K`,
29/// ignoring the key and using the `index` parameter instead. Collections that
30/// are indexed by key (like `HashMap`) use the `key` parameter.
31pub trait KeyedAccess<K> {
32    /// Collection values.
33    type Value;
34    /// Acquire read-only access to a value.
35    fn keyed(&self, index: usize, key: &K) -> &Self::Value;
36    /// Acquire mutable access to a value.
37    fn keyed_mut(&mut self, index: usize, key: &K) -> &mut Self::Value;
38}
39impl<K, T> KeyedAccess<K> for VecDeque<T> {
40    type Value = T;
41    fn keyed(&self, index: usize, _key: &K) -> &Self::Value {
42        self.index(index)
43    }
44    fn keyed_mut(&mut self, index: usize, _key: &K) -> &mut Self::Value {
45        self.index_mut(index)
46    }
47}
48impl<K, T> KeyedAccess<K> for Vec<T> {
49    type Value = T;
50    fn keyed(&self, index: usize, _key: &K) -> &Self::Value {
51        self.index(index)
52    }
53    fn keyed_mut(&mut self, index: usize, _key: &K) -> &mut Self::Value {
54        self.index_mut(index)
55    }
56}
57impl<K, T> KeyedAccess<K> for [T] {
58    type Value = T;
59    fn keyed(&self, index: usize, _key: &K) -> &Self::Value {
60        self.index(index)
61    }
62    fn keyed_mut(&mut self, index: usize, _key: &K) -> &mut Self::Value {
63        self.index_mut(index)
64    }
65}
66impl<K: Ord, V> KeyedAccess<K> for std::collections::BTreeMap<K, V> {
67    type Value = V;
68    fn keyed(&self, _index: usize, key: &K) -> &Self::Value {
69        self.get(key).expect("key does not exist")
70    }
71    fn keyed_mut(&mut self, _index: usize, key: &K) -> &mut Self::Value {
72        self.get_mut(key).expect("key does not exist")
73    }
74}
75impl<K: Hash + Eq, V> KeyedAccess<K> for std::collections::HashMap<K, V> {
76    type Value = V;
77    fn keyed(&self, _index: usize, key: &K) -> &Self::Value {
78        self.get(key).expect("key does not exist")
79    }
80    fn keyed_mut(&mut self, _index: usize, key: &K) -> &mut Self::Value {
81        self.get_mut(key).expect("key does not exist")
82    }
83}
84
85/// Provides access to a subfield that contains some kind of keyed collection.
86#[derive(Debug)]
87pub struct KeyedSubfield<Inner, Prev, K, T>
88where
89    for<'a> &'a T: IntoIterator,
90{
91    #[cfg(any(debug_assertions, leptos_debuginfo))]
92    defined_at: &'static Location<'static>,
93    path_segment: StorePathSegment,
94    inner: Inner,
95    read: fn(&Prev) -> &T,
96    write: fn(&mut Prev) -> &mut T,
97    pub(crate) key_fn: fn(<&T as IntoIterator>::Item) -> K,
98}
99
100impl<Inner, Prev, K, T> Clone for KeyedSubfield<Inner, Prev, K, T>
101where
102    for<'a> &'a T: IntoIterator,
103    Inner: Clone,
104{
105    fn clone(&self) -> Self {
106        Self {
107            #[cfg(any(debug_assertions, leptos_debuginfo))]
108            defined_at: self.defined_at,
109            path_segment: self.path_segment,
110            inner: self.inner.clone(),
111            read: self.read,
112            write: self.write,
113            key_fn: self.key_fn,
114        }
115    }
116}
117
118impl<Inner, Prev, K, T> Copy for KeyedSubfield<Inner, Prev, K, T>
119where
120    for<'a> &'a T: IntoIterator,
121    Inner: Copy,
122{
123}
124
125impl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>
126where
127    for<'a> &'a T: IntoIterator,
128{
129    /// Creates a keyed subfield of the inner data type with the given key function.
130    #[track_caller]
131    pub fn new(
132        inner: Inner,
133        path_segment: StorePathSegment,
134        key_fn: fn(<&T as IntoIterator>::Item) -> K,
135        read: fn(&Prev) -> &T,
136        write: fn(&mut Prev) -> &mut T,
137    ) -> Self {
138        Self {
139            #[cfg(any(debug_assertions, leptos_debuginfo))]
140            defined_at: Location::caller(),
141            inner,
142            path_segment,
143            read,
144            write,
145            key_fn,
146        }
147    }
148}
149
150impl<Inner, Prev, K, T> StoreField for KeyedSubfield<Inner, Prev, K, T>
151where
152    Self: Clone,
153    for<'a> &'a T: IntoIterator,
154    Inner: StoreField<Value = Prev>,
155    Prev: 'static,
156    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
157{
158    type Value = T;
159    type Reader = Mapped<Inner::Reader, T>;
160    type Writer = MappedMut<WriteGuard<Vec<ArcTrigger>, Inner::Writer>, T>;
161
162    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
163        self.inner
164            .path()
165            .into_iter()
166            .chain(iter::once(self.path_segment))
167    }
168
169    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {
170        self.inner
171            .path_unkeyed()
172            .into_iter()
173            .chain(iter::once(self.path_segment))
174    }
175
176    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {
177        self.inner.get_trigger(path)
178    }
179
180    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {
181        self.inner.get_trigger_unkeyed(path)
182    }
183
184    fn reader(&self) -> Option<Self::Reader> {
185        let inner = self.inner.reader()?;
186        Some(Mapped::new_with_guard(inner, self.read))
187    }
188
189    fn writer(&self) -> Option<Self::Writer> {
190        let mut parent = self.inner.writer()?;
191        parent.untrack();
192        let triggers = self.triggers_for_current_path();
193        let guard = WriteGuard::new(triggers, parent);
194        Some(MappedMut::new(guard, self.read, self.write))
195    }
196
197    #[inline(always)]
198    fn keys(&self) -> Option<KeyMap> {
199        self.inner.keys()
200    }
201
202    fn track_field(&self) {
203        let mut full_path = self.path().into_iter().collect::<StorePath>();
204        let trigger = self.get_trigger(self.path().into_iter().collect());
205        trigger.this.track();
206        trigger.children.track();
207
208        // tracks `this` for all ancestors: i.e., it will track any change that is made
209        // directly to one of its ancestors, but not a change made to a *child* of an ancestor
210        // (which would end up with every subfield tracking its own siblings, because they are
211        // children of its parent)
212        while !full_path.is_empty() {
213            full_path.pop();
214            let inner = self.get_trigger(full_path.clone());
215            inner.this.track();
216        }
217    }
218}
219
220impl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>
221where
222    Self: Clone,
223    for<'a> &'a T: IntoIterator,
224    Inner: StoreField<Value = Prev>,
225    Prev: 'static,
226    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
227{
228    fn latest_keys(&self) -> Vec<K> {
229        self.reader()
230            .map(|r| r.deref().into_iter().map(|n| (self.key_fn)(n)).collect())
231            .unwrap_or_default()
232    }
233
234    pub(crate) fn path_at_key(
235        &self,
236        base_path: &StorePath,
237        key: &K,
238    ) -> Option<StorePath> {
239        let keys = self.keys();
240        let keys = keys.as_ref()?;
241        let segment = keys
242            .with_field_keys(
243                base_path.clone(),
244                |keys| (keys.get(key), vec![]),
245                || self.latest_keys(),
246            )
247            .flatten()
248            .map(|(_, idx)| idx)?;
249        let mut path = base_path.clone();
250        path.push(segment);
251        Some(path)
252    }
253}
254
255impl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>
256where
257    Self: Clone,
258    for<'a> &'a T: IntoIterator,
259    Inner: StoreField<Value = Prev>,
260{
261    /// Keyed access to a keyed subfield of a store.
262    pub fn at_key(&self, key: K) -> AtKeyed<Inner, Prev, K, T> {
263        AtKeyed::new(self.clone(), key)
264    }
265}
266
267/// Gives keyed write access to a value in some collection.
268pub struct KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>
269where
270    KeyedSubfield<Inner, Prev, K, T>: Clone,
271    for<'a> &'a T: IntoIterator,
272    Inner: StoreField<Value = Prev>,
273    Prev: 'static,
274    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
275{
276    inner: KeyedSubfield<Inner, Prev, K, T>,
277    guard: Option<Guard>,
278    untracked: bool,
279}
280
281impl<Inner, Prev, K, T, Guard> Deref
282    for KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>
283where
284    Guard: Deref,
285    KeyedSubfield<Inner, Prev, K, T>: Clone,
286    for<'a> &'a T: IntoIterator,
287    Inner: StoreField<Value = Prev>,
288    Prev: 'static,
289    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
290{
291    type Target = Guard::Target;
292
293    fn deref(&self) -> &Self::Target {
294        self.guard
295            .as_ref()
296            .expect("should be Some(_) until dropped")
297            .deref()
298    }
299}
300
301impl<Inner, Prev, K, T, Guard> DerefMut
302    for KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>
303where
304    Guard: DerefMut,
305    KeyedSubfield<Inner, Prev, K, T>: Clone,
306    for<'a> &'a T: IntoIterator,
307    Inner: StoreField<Value = Prev>,
308    Prev: 'static,
309    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
310{
311    fn deref_mut(&mut self) -> &mut Self::Target {
312        self.guard
313            .as_mut()
314            .expect("should be Some(_) until dropped")
315            .deref_mut()
316    }
317}
318
319impl<Inner, Prev, K, T, Guard> UntrackableGuard
320    for KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>
321where
322    Guard: UntrackableGuard,
323    KeyedSubfield<Inner, Prev, K, T>: Clone,
324    for<'a> &'a T: IntoIterator,
325    Inner: StoreField<Value = Prev>,
326    Prev: 'static,
327    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
328{
329    fn untrack(&mut self) {
330        self.untracked = true;
331        if let Some(inner) = self.guard.as_mut() {
332            inner.untrack();
333        }
334    }
335}
336
337impl<Inner, Prev, K, T, Guard> Drop
338    for KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>
339where
340    KeyedSubfield<Inner, Prev, K, T>: Clone,
341    for<'a> &'a T: IntoIterator,
342    Inner: StoreField<Value = Prev>,
343    Prev: 'static,
344    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
345{
346    fn drop(&mut self) {
347        // dropping the inner guard will
348        // 1) synchronously release its write lock on the store's value
349        // 2) trigger an (asynchronous) reactive update
350        drop(self.guard.take());
351
352        // now that the write lock is release, we can get a read lock to refresh this keyed field
353        // based on the new value
354        self.inner.update_keys();
355
356        if !self.untracked {
357            self.inner.notify();
358        }
359
360        // reactive updates happen on the next tick
361    }
362}
363
364impl<Inner, Prev, K, T> DefinedAt for KeyedSubfield<Inner, Prev, K, T>
365where
366    for<'a> &'a T: IntoIterator,
367    Inner: StoreField<Value = Prev>,
368{
369    fn defined_at(&self) -> Option<&'static Location<'static>> {
370        #[cfg(any(debug_assertions, leptos_debuginfo))]
371        {
372            Some(self.defined_at)
373        }
374        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
375        {
376            None
377        }
378    }
379}
380
381impl<Inner, Prev, K, T> IsDisposed for KeyedSubfield<Inner, Prev, K, T>
382where
383    for<'a> &'a T: IntoIterator,
384    Inner: IsDisposed,
385{
386    fn is_disposed(&self) -> bool {
387        self.inner.is_disposed()
388    }
389}
390
391impl<Inner, Prev, K, T> Notify for KeyedSubfield<Inner, Prev, K, T>
392where
393    Self: Clone,
394    for<'a> &'a T: IntoIterator,
395    Inner: StoreField<Value = Prev>,
396    Prev: 'static,
397    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
398{
399    fn notify(&self) {
400        let trigger = self.get_trigger(self.path().into_iter().collect());
401        trigger.this.notify();
402        trigger.children.notify();
403    }
404}
405
406impl<Inner, Prev, K, T> Track for KeyedSubfield<Inner, Prev, K, T>
407where
408    Self: Clone,
409    for<'a> &'a T: IntoIterator,
410    Inner: StoreField<Value = Prev> + Track + 'static,
411    Prev: 'static,
412    T: 'static,
413    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
414{
415    fn track(&self) {
416        self.track_field();
417    }
418}
419
420impl<Inner, Prev, K, T> ReadUntracked for KeyedSubfield<Inner, Prev, K, T>
421where
422    Self: Clone,
423    for<'a> &'a T: IntoIterator,
424    Inner: StoreField<Value = Prev>,
425    Prev: 'static,
426    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
427{
428    type Value = <Self as StoreField>::Reader;
429
430    fn try_read_untracked(&self) -> Option<Self::Value> {
431        self.reader()
432    }
433}
434
435impl<Inner, Prev, K, T> Write for KeyedSubfield<Inner, Prev, K, T>
436where
437    Self: Clone,
438    for<'a> &'a T: IntoIterator,
439    T: 'static,
440    Inner: StoreField<Value = Prev>,
441    Prev: 'static,
442    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
443{
444    type Value = T;
445
446    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
447        let guard = self.writer()?;
448        Some(KeyedSubfieldWriteGuard {
449            inner: self.clone(),
450            guard: Some(guard),
451            untracked: false,
452        })
453    }
454
455    fn try_write_untracked(
456        &self,
457    ) -> Option<impl DerefMut<Target = Self::Value>> {
458        let mut guard = self.writer()?;
459        guard.untrack();
460        Some(KeyedSubfieldWriteGuard {
461            inner: self.clone(),
462            guard: Some(guard),
463            untracked: true,
464        })
465    }
466}
467
468/// Gives access to the value in a collection based on some key.
469#[derive(Debug)]
470pub struct AtKeyed<Inner, Prev, K, T>
471where
472    for<'a> &'a T: IntoIterator,
473{
474    #[cfg(any(debug_assertions, leptos_debuginfo))]
475    defined_at: &'static Location<'static>,
476    inner: KeyedSubfield<Inner, Prev, K, T>,
477    key: K,
478}
479
480impl<Inner, Prev, K, T> AtKeyed<Inner, Prev, K, T>
481where
482    for<'a> &'a T: IntoIterator,
483    K: Clone,
484{
485    /// Key used for keyed collection access.
486    pub fn key(&self) -> K {
487        self.key.clone()
488    }
489}
490
491impl<Inner, Prev, K, T> Clone for AtKeyed<Inner, Prev, K, T>
492where
493    for<'a> &'a T: IntoIterator,
494    KeyedSubfield<Inner, Prev, K, T>: Clone,
495    K: Debug + Clone,
496{
497    fn clone(&self) -> Self {
498        Self {
499            #[cfg(any(debug_assertions, leptos_debuginfo))]
500            defined_at: self.defined_at,
501            inner: self.inner.clone(),
502            key: self.key.clone(),
503        }
504    }
505}
506
507impl<Inner, Prev, K, T> Copy for AtKeyed<Inner, Prev, K, T>
508where
509    for<'a> &'a T: IntoIterator,
510    KeyedSubfield<Inner, Prev, K, T>: Copy,
511    K: Debug + Copy,
512{
513}
514
515impl<Inner, Prev, K, T> AtKeyed<Inner, Prev, K, T>
516where
517    for<'a> &'a T: IntoIterator,
518{
519    /// Provides access to the item in the inner collection at this key.
520    #[track_caller]
521    pub fn new(inner: KeyedSubfield<Inner, Prev, K, T>, key: K) -> Self {
522        Self {
523            #[cfg(any(debug_assertions, leptos_debuginfo))]
524            defined_at: Location::caller(),
525            inner,
526            key,
527        }
528    }
529}
530
531impl<Inner, Prev, K, T> AtKeyed<Inner, Prev, K, T>
532where
533    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
534    KeyedSubfield<Inner, Prev, K, T>: Clone,
535    for<'a> &'a T: IntoIterator,
536    Inner: StoreField<Value = Prev>,
537    Prev: 'static,
538    T: KeyedAccess<K>,
539    T::Value: Sized,
540{
541    /// Attempt to resolve the inner index if is still exists.
542    fn resolve_index(&self) -> Option<usize> {
543        let inner_path = self.inner.path().into_iter().collect();
544        let keys = self.inner.keys()?;
545        keys.with_field_keys(
546            inner_path,
547            |keys| (keys.get(&self.key), vec![]),
548            || self.inner.latest_keys(),
549        )
550        .flatten()
551        .map(|(_, idx)| idx)
552    }
553}
554
555impl<Inner, Prev, K, T> StoreField for AtKeyed<Inner, Prev, K, T>
556where
557    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
558    KeyedSubfield<Inner, Prev, K, T>: Clone,
559    for<'a> &'a T: IntoIterator,
560    Inner: StoreField<Value = Prev>,
561    Prev: 'static,
562    T: KeyedAccess<K>,
563    T::Value: Sized,
564{
565    type Value = T::Value;
566    type Reader = MappedMutArc<
567        <KeyedSubfield<Inner, Prev, K, T> as StoreField>::Reader,
568        T::Value,
569    >;
570    type Writer = WriteGuard<
571        Vec<ArcTrigger>,
572        MappedMutArc<
573            <KeyedSubfield<Inner, Prev, K, T> as StoreField>::Writer,
574            T::Value,
575        >,
576    >;
577
578    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
579        let inner = self.inner.path().into_iter().collect::<StorePath>();
580        let keys = self
581            .inner
582            .keys()
583            .expect("using keys on a store with no keys");
584        let this = keys
585            .with_field_keys(
586                inner.clone(),
587                |keys| (keys.get(&self.key), vec![]),
588                || self.inner.latest_keys(),
589            )
590            .flatten()
591            .map(|(path, _)| path);
592        inner.into_iter().chain(this)
593    }
594
595    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {
596        let inner =
597            self.inner.path_unkeyed().into_iter().collect::<StorePath>();
598        let keys = self
599            .inner
600            .keys()
601            .expect("using keys on a store with no keys");
602        let this = keys
603            .with_field_keys(
604                inner.clone(),
605                |keys| (keys.get(&self.key), vec![]),
606                || self.inner.latest_keys(),
607            )
608            .flatten()
609            .map(|(_, idx)| StorePathSegment(idx));
610        inner.into_iter().chain(this)
611    }
612
613    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {
614        self.inner.get_trigger(path)
615    }
616
617    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {
618        self.inner.get_trigger_unkeyed(path)
619    }
620
621    fn reader(&self) -> Option<Self::Reader> {
622        let inner = self.inner.reader()?;
623        let index = self.resolve_index()?;
624        Some(MappedMutArc::new(
625            inner,
626            {
627                let key = self.key.clone();
628                move |n| n.keyed(index, &key)
629            },
630            {
631                let key = self.key.clone();
632                move |n| n.keyed_mut(index, &key)
633            },
634        ))
635    }
636
637    fn writer(&self) -> Option<Self::Writer> {
638        let mut inner = self.inner.writer()?;
639        inner.untrack();
640        let index = self.resolve_index()?;
641        let triggers = self.triggers_for_current_path();
642        Some(WriteGuard::new(
643            triggers,
644            MappedMutArc::new(
645                inner,
646                {
647                    let key = self.key.clone();
648                    move |n| n.keyed(index, &key)
649                },
650                {
651                    let key = self.key.clone();
652                    move |n| n.keyed_mut(index, &key)
653                },
654            ),
655        ))
656    }
657
658    #[inline(always)]
659    fn keys(&self) -> Option<KeyMap> {
660        self.inner.keys()
661    }
662
663    fn track_field(&self) {
664        let mut full_path = self.path().into_iter().collect::<StorePath>();
665        let trigger = self.get_trigger(self.path().into_iter().collect());
666        trigger.this.track();
667        trigger.children.track();
668
669        // tracks `this` for all ancestors: i.e., it will track any change that is made
670        // directly to one of its ancestors, but not a change made to a *child* of an ancestor
671        // (which would end up with every subfield tracking its own siblings, because they are
672        // children of its parent)
673        while !full_path.is_empty() {
674            full_path.pop();
675            let inner = self.get_trigger(full_path.clone());
676            inner.this.track();
677        }
678    }
679}
680
681impl<Inner, Prev, K, T> DefinedAt for AtKeyed<Inner, Prev, K, T>
682where
683    for<'a> &'a T: IntoIterator,
684{
685    fn defined_at(&self) -> Option<&'static Location<'static>> {
686        #[cfg(any(debug_assertions, leptos_debuginfo))]
687        {
688            Some(self.defined_at)
689        }
690        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
691        {
692            None
693        }
694    }
695}
696
697impl<Inner, Prev, K, T> IsDisposed for AtKeyed<Inner, Prev, K, T>
698where
699    for<'a> &'a T: IntoIterator,
700    Inner: IsDisposed,
701{
702    fn is_disposed(&self) -> bool {
703        self.inner.is_disposed()
704    }
705}
706
707impl<Inner, Prev, K, T> Notify for AtKeyed<Inner, Prev, K, T>
708where
709    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
710    KeyedSubfield<Inner, Prev, K, T>: Clone,
711    for<'a> &'a T: IntoIterator,
712    Inner: StoreField<Value = Prev>,
713    Prev: 'static,
714    T: KeyedAccess<K>,
715    T::Value: Sized,
716{
717    fn notify(&self) {
718        let trigger = self.get_trigger(self.path().into_iter().collect());
719        trigger.this.notify();
720        trigger.children.notify();
721    }
722}
723
724impl<Inner, Prev, K, T> Track for AtKeyed<Inner, Prev, K, T>
725where
726    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
727    KeyedSubfield<Inner, Prev, K, T>: Clone,
728    for<'a> &'a T: IntoIterator,
729    Inner: StoreField<Value = Prev>,
730    Prev: 'static,
731    T: KeyedAccess<K>,
732    T::Value: Sized,
733{
734    fn track(&self) {
735        self.track_field();
736    }
737}
738
739impl<Inner, Prev, K, T> ReadUntracked for AtKeyed<Inner, Prev, K, T>
740where
741    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
742    KeyedSubfield<Inner, Prev, K, T>: Clone,
743    for<'a> &'a T: IntoIterator,
744    Inner: StoreField<Value = Prev>,
745    Prev: 'static,
746    T: KeyedAccess<K>,
747    T::Value: Sized,
748{
749    type Value = <Self as StoreField>::Reader;
750
751    fn try_read_untracked(&self) -> Option<Self::Value> {
752        self.reader()
753    }
754}
755
756impl<Inner, Prev, K, T> Write for AtKeyed<Inner, Prev, K, T>
757where
758    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
759    KeyedSubfield<Inner, Prev, K, T>: Clone,
760    for<'a> &'a T: IntoIterator,
761    Inner: StoreField<Value = Prev>,
762    Prev: 'static,
763    T: KeyedAccess<K>,
764    T::Value: Sized + 'static,
765{
766    type Value = T::Value;
767
768    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
769        self.writer()
770    }
771
772    fn try_write_untracked(
773        &self,
774    ) -> Option<impl DerefMut<Target = Self::Value>> {
775        self.writer().map(|mut writer| {
776            writer.untrack();
777            writer
778        })
779    }
780}
781
782impl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>
783where
784    Self: Clone,
785    for<'a> &'a T: IntoIterator,
786    Inner: StoreField<Value = Prev>,
787    Prev: 'static,
788    K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
789{
790    /// Generates a new set of keys and registers those keys with the parent store.
791    pub fn update_keys(&self) {
792        let inner_path = self.path().into_iter().collect();
793        let keys = self
794            .inner
795            .keys()
796            .expect("updating keys on a store with no keys");
797
798        // generating the latest keys out here means that if we have
799        // nested keyed fields, the second field will not try to take a
800        // read-lock on the key map to get the field while the first field
801        // is still holding the write-lock in the closure below
802        let latest = self.latest_keys();
803        keys.with_field_keys(
804            inner_path,
805            |keys| ((), keys.update(latest)),
806            || self.latest_keys(),
807        );
808    }
809}
810
811impl<Inner, Prev, K, T> IntoIterator for KeyedSubfield<Inner, Prev, K, T>
812where
813    Self: Clone,
814    for<'a> &'a T: IntoIterator,
815    Inner: Clone + StoreField<Value = Prev> + 'static,
816    Prev: 'static,
817    K: Clone + Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
818    T: KeyedAccess<K> + 'static,
819    T::Value: Sized,
820{
821    type Item = AtKeyed<Inner, Prev, K, T>;
822    type IntoIter = StoreFieldKeyedIter<Inner, Prev, K, T>;
823
824    #[track_caller]
825    fn into_iter(self) -> StoreFieldKeyedIter<Inner, Prev, K, T> {
826        // reactively track changes to this field
827        self.update_keys();
828        self.track_field();
829
830        // get the current length of the field by accessing slice
831        let reader = self.reader();
832
833        let keys = reader
834            .map(|r| {
835                r.into_iter()
836                    .map(|item| (self.key_fn)(item))
837                    .collect::<VecDeque<_>>()
838            })
839            .unwrap_or_default();
840
841        // return the iterator
842        StoreFieldKeyedIter { inner: self, keys }
843    }
844}
845
846/// An iterator over a [`KeyedSubfield`].
847pub struct StoreFieldKeyedIter<Inner, Prev, K, T>
848where
849    for<'a> &'a T: IntoIterator,
850    T: KeyedAccess<K>,
851{
852    inner: KeyedSubfield<Inner, Prev, K, T>,
853    keys: VecDeque<K>,
854}
855
856impl<Inner, Prev, K, T> Iterator for StoreFieldKeyedIter<Inner, Prev, K, T>
857where
858    Inner: StoreField<Value = Prev> + Clone + 'static,
859    T: KeyedAccess<K> + 'static,
860    for<'a> &'a T: IntoIterator,
861{
862    type Item = AtKeyed<Inner, Prev, K, T>;
863
864    fn next(&mut self) -> Option<Self::Item> {
865        self.keys
866            .pop_front()
867            .map(|key| AtKeyed::new(self.inner.clone(), key))
868    }
869}
870
871impl<Inner, Prev, K, T> DoubleEndedIterator
872    for StoreFieldKeyedIter<Inner, Prev, K, T>
873where
874    Inner: StoreField<Value = Prev> + Clone + 'static,
875    T: KeyedAccess<K> + 'static,
876    T::Value: Sized + 'static,
877    for<'a> &'a T: IntoIterator,
878{
879    fn next_back(&mut self) -> Option<Self::Item> {
880        self.keys
881            .pop_back()
882            .map(|key| AtKeyed::new(self.inner.clone(), key))
883    }
884}
885
886#[cfg(test)]
887mod tests {
888    use crate::{self as reactive_stores, tests::tick, AtKeyed, Store};
889    use reactive_graph::{
890        effect::Effect,
891        traits::{Get, GetUntracked, ReadUntracked, Set, Track, Write},
892    };
893    use reactive_stores::Patch;
894    use std::{
895        collections::{BTreeMap, BTreeSet, HashMap},
896        sync::{
897            atomic::{AtomicUsize, Ordering},
898            Arc,
899        },
900    };
901
902    #[derive(Debug, Store, Default, Patch)]
903    struct TodoVec {
904        #[store(key: usize = |todo| todo.id)]
905        todos: Vec<Todo>,
906    }
907    impl TodoVec {
908        fn test_data() -> Self {
909            Self {
910                todos: vec![
911                    Todo {
912                        id: 10,
913                        label: "A".to_string(),
914                    },
915                    Todo {
916                        id: 11,
917                        label: "B".to_string(),
918                    },
919                    Todo {
920                        id: 12,
921                        label: "C".to_string(),
922                    },
923                ],
924            }
925        }
926    }
927
928    #[derive(Debug, Store, Default)]
929    struct TodoBTreeMap {
930        #[store(key: usize = |(key, _)| *key)]
931        todos: BTreeMap<usize, Todo>,
932    }
933    impl TodoBTreeMap {
934        fn test_data() -> Self {
935            Self {
936                todos: [
937                    Todo {
938                        id: 10,
939                        label: "A".to_string(),
940                    },
941                    Todo {
942                        id: 11,
943                        label: "B".to_string(),
944                    },
945                    Todo {
946                        id: 12,
947                        label: "C".to_string(),
948                    },
949                ]
950                .into_iter()
951                .map(|todo| (todo.id, todo))
952                .collect(),
953            }
954        }
955    }
956
957    #[derive(Debug, Store, Default)]
958    struct TodoHashMap {
959        #[store(key: String = |(key, _)| key.clone())]
960        todos: HashMap<String, Todo>,
961    }
962    impl TodoHashMap {
963        fn test_data() -> Self {
964            Self {
965                todos: [
966                    Todo {
967                        id: 10,
968                        label: "A".to_string(),
969                    },
970                    Todo {
971                        id: 11,
972                        label: "B".to_string(),
973                    },
974                    Todo {
975                        id: 12,
976                        label: "C".to_string(),
977                    },
978                ]
979                .into_iter()
980                .map(|todo| (todo.label.clone(), todo))
981                .collect(),
982            }
983        }
984    }
985
986    #[derive(
987        Debug, Store, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Patch,
988    )]
989    struct Todo {
990        id: usize,
991        label: String,
992    }
993
994    impl Todo {
995        pub fn new(id: usize, label: impl ToString) -> Self {
996            Self {
997                id,
998                label: label.to_string(),
999            }
1000        }
1001    }
1002
1003    #[tokio::test]
1004    async fn keyed_fields_can_be_moved() {
1005        _ = any_spawner::Executor::init_tokio();
1006
1007        let store = Store::new(TodoVec::test_data());
1008        assert_eq!(store.read_untracked().todos.len(), 3);
1009
1010        // create an effect to read from each keyed field
1011        let a_count = Arc::new(AtomicUsize::new(0));
1012        let b_count = Arc::new(AtomicUsize::new(0));
1013        let c_count = Arc::new(AtomicUsize::new(0));
1014
1015        let a = AtKeyed::new(store.todos(), 10);
1016        let b = AtKeyed::new(store.todos(), 11);
1017        let c = AtKeyed::new(store.todos(), 12);
1018
1019        Effect::new_sync({
1020            let a_count = Arc::clone(&a_count);
1021            move || {
1022                a.track();
1023                a_count.fetch_add(1, Ordering::Relaxed);
1024            }
1025        });
1026        Effect::new_sync({
1027            let b_count = Arc::clone(&b_count);
1028            move || {
1029                b.track();
1030                b_count.fetch_add(1, Ordering::Relaxed);
1031            }
1032        });
1033        Effect::new_sync({
1034            let c_count = Arc::clone(&c_count);
1035            move || {
1036                c.track();
1037                c_count.fetch_add(1, Ordering::Relaxed);
1038            }
1039        });
1040
1041        tick().await;
1042        assert_eq!(a_count.load(Ordering::Relaxed), 1);
1043        assert_eq!(b_count.load(Ordering::Relaxed), 1);
1044        assert_eq!(c_count.load(Ordering::Relaxed), 1);
1045
1046        // writing at a key doesn't notify siblings
1047        *a.label().write() = "Foo".into();
1048        tick().await;
1049        assert_eq!(a_count.load(Ordering::Relaxed), 2);
1050        assert_eq!(b_count.load(Ordering::Relaxed), 1);
1051        assert_eq!(c_count.load(Ordering::Relaxed), 1);
1052
1053        // the keys can be reorganized
1054        store.todos().write().swap(0, 2);
1055        let after = store.todos().get_untracked();
1056        assert_eq!(
1057            after,
1058            vec![Todo::new(12, "C"), Todo::new(11, "B"), Todo::new(10, "Foo")]
1059        );
1060
1061        tick().await;
1062        assert_eq!(a_count.load(Ordering::Relaxed), 3);
1063        assert_eq!(b_count.load(Ordering::Relaxed), 2);
1064        assert_eq!(c_count.load(Ordering::Relaxed), 2);
1065
1066        // and after we move the keys around, they still update the moved items
1067        a.label().set("Bar".into());
1068        let after = store.todos().get_untracked();
1069        assert_eq!(
1070            after,
1071            vec![Todo::new(12, "C"), Todo::new(11, "B"), Todo::new(10, "Bar")]
1072        );
1073        tick().await;
1074        assert_eq!(a_count.load(Ordering::Relaxed), 4);
1075        assert_eq!(b_count.load(Ordering::Relaxed), 2);
1076        assert_eq!(c_count.load(Ordering::Relaxed), 2);
1077
1078        // we can remove a key and add a new one
1079        store.todos().write().pop();
1080        store.todos().write().push(Todo::new(13, "New"));
1081        let after = store.todos().get_untracked();
1082        assert_eq!(
1083            after,
1084            vec![Todo::new(12, "C"), Todo::new(11, "B"), Todo::new(13, "New")]
1085        );
1086        tick().await;
1087        assert_eq!(a_count.load(Ordering::Relaxed), 5);
1088        assert_eq!(b_count.load(Ordering::Relaxed), 3);
1089        assert_eq!(c_count.load(Ordering::Relaxed), 3);
1090    }
1091
1092    #[tokio::test]
1093    async fn untracked_write_on_keyed_subfield_shouldnt_notify() {
1094        _ = any_spawner::Executor::init_tokio();
1095
1096        let store = Store::new(TodoVec::test_data());
1097        assert_eq!(store.read_untracked().todos.len(), 3);
1098
1099        // create an effect to read from the keyed subfield
1100        let todos_count = Arc::new(AtomicUsize::new(0));
1101        Effect::new_sync({
1102            let todos_count = Arc::clone(&todos_count);
1103            move || {
1104                store.todos().track();
1105                todos_count.fetch_add(1, Ordering::Relaxed);
1106            }
1107        });
1108
1109        tick().await;
1110        assert_eq!(todos_count.load(Ordering::Relaxed), 1);
1111
1112        // writing to keyed subfield notifies the iterator
1113        store.todos().write().push(Todo {
1114            id: 13,
1115            label: "D".into(),
1116        });
1117        tick().await;
1118        assert_eq!(todos_count.load(Ordering::Relaxed), 2);
1119
1120        // but an untracked write doesn't
1121        store.todos().write_untracked().push(Todo {
1122            id: 14,
1123            label: "E".into(),
1124        });
1125        tick().await;
1126        assert_eq!(todos_count.load(Ordering::Relaxed), 2);
1127    }
1128
1129    #[tokio::test]
1130    async fn btree_keyed_fields_can_be_moved() {
1131        _ = any_spawner::Executor::init_tokio();
1132
1133        let store = Store::new(TodoBTreeMap::test_data());
1134        assert_eq!(store.read_untracked().todos.len(), 3);
1135
1136        // create an effect to read from each keyed field
1137        let a_count = Arc::new(AtomicUsize::new(0));
1138        let b_count = Arc::new(AtomicUsize::new(0));
1139        let c_count = Arc::new(AtomicUsize::new(0));
1140
1141        let a = AtKeyed::new(store.todos(), 10);
1142        let b = AtKeyed::new(store.todos(), 11);
1143        let c = AtKeyed::new(store.todos(), 12);
1144
1145        Effect::new_sync({
1146            let a_count = Arc::clone(&a_count);
1147            move || {
1148                a.track();
1149                a_count.fetch_add(1, Ordering::Relaxed);
1150            }
1151        });
1152        Effect::new_sync({
1153            let b_count = Arc::clone(&b_count);
1154            move || {
1155                b.track();
1156                b_count.fetch_add(1, Ordering::Relaxed);
1157            }
1158        });
1159        Effect::new_sync({
1160            let c_count = Arc::clone(&c_count);
1161            move || {
1162                c.track();
1163                c_count.fetch_add(1, Ordering::Relaxed);
1164            }
1165        });
1166
1167        tick().await;
1168        assert_eq!(a_count.load(Ordering::Relaxed), 1);
1169        assert_eq!(b_count.load(Ordering::Relaxed), 1);
1170        assert_eq!(c_count.load(Ordering::Relaxed), 1);
1171
1172        // writing at a key doesn't notify siblings
1173        *a.label().write() = "Foo".into();
1174        tick().await;
1175        assert_eq!(a_count.load(Ordering::Relaxed), 2);
1176        assert_eq!(b_count.load(Ordering::Relaxed), 1);
1177        assert_eq!(c_count.load(Ordering::Relaxed), 1);
1178        let after = store.todos().get_untracked();
1179        assert_eq!(
1180            after.values().cloned().collect::<Vec<_>>(),
1181            vec![Todo::new(10, "Foo"), Todo::new(11, "B"), Todo::new(12, "C"),]
1182        );
1183
1184        a.label().set("Bar".into());
1185        let after = store.todos().get_untracked();
1186        assert_eq!(
1187            after.values().cloned().collect::<Vec<_>>(),
1188            vec![Todo::new(10, "Bar"), Todo::new(11, "B"), Todo::new(12, "C"),]
1189        );
1190        tick().await;
1191        assert_eq!(a_count.load(Ordering::Relaxed), 3);
1192        assert_eq!(b_count.load(Ordering::Relaxed), 1);
1193        assert_eq!(c_count.load(Ordering::Relaxed), 1);
1194
1195        // we can remove a key and add a new one
1196        store.todos().write().remove(&12);
1197        store.todos().write().insert(13, Todo::new(13, "New"));
1198        let after = store.todos().get_untracked();
1199        assert_eq!(
1200            after.values().cloned().collect::<Vec<_>>(),
1201            vec![
1202                Todo::new(10, "Bar"),
1203                Todo::new(11, "B"),
1204                Todo::new(13, "New")
1205            ]
1206        );
1207        tick().await;
1208        assert_eq!(a_count.load(Ordering::Relaxed), 4);
1209        assert_eq!(b_count.load(Ordering::Relaxed), 2);
1210        assert_eq!(c_count.load(Ordering::Relaxed), 2);
1211
1212        assert_eq!(
1213            after.keys().copied().collect::<BTreeSet<usize>>(),
1214            BTreeSet::from([10, 11, 13])
1215        );
1216
1217        let at_existing_key = AtKeyed::new(store.todos(), 13);
1218        let existing = at_existing_key.try_get();
1219        assert!(existing.is_some());
1220        assert_eq!(existing, Some(Todo::new(13, "New")));
1221
1222        let at_faulty_key = AtKeyed::new(store.todos(), 999);
1223        let missing = at_faulty_key.try_get();
1224        assert!(missing.is_none(), "faulty key should return none.")
1225    }
1226
1227    #[tokio::test]
1228    async fn hashmap_keyed_fields_can_be_moved() {
1229        _ = any_spawner::Executor::init_tokio();
1230
1231        let store = Store::new(TodoHashMap::test_data());
1232        assert_eq!(store.read_untracked().todos.len(), 3);
1233
1234        // create an effect to read from each keyed field
1235        let a_count = Arc::new(AtomicUsize::new(0));
1236        let b_count = Arc::new(AtomicUsize::new(0));
1237        let c_count = Arc::new(AtomicUsize::new(0));
1238
1239        let a = AtKeyed::new(store.todos(), "A".to_string());
1240        let b = AtKeyed::new(store.todos(), "B".to_string());
1241        let c = AtKeyed::new(store.todos(), "C".to_string());
1242
1243        Effect::new_sync({
1244            let a_count = Arc::clone(&a_count);
1245            let a = a.clone();
1246            move || {
1247                a.track();
1248                a_count.fetch_add(1, Ordering::Relaxed);
1249            }
1250        });
1251        Effect::new_sync({
1252            let b_count = Arc::clone(&b_count);
1253            move || {
1254                b.track();
1255                b_count.fetch_add(1, Ordering::Relaxed);
1256            }
1257        });
1258        Effect::new_sync({
1259            let c_count = Arc::clone(&c_count);
1260            move || {
1261                c.track();
1262                c_count.fetch_add(1, Ordering::Relaxed);
1263            }
1264        });
1265
1266        tick().await;
1267        assert_eq!(a_count.load(Ordering::Relaxed), 1);
1268        assert_eq!(b_count.load(Ordering::Relaxed), 1);
1269        assert_eq!(c_count.load(Ordering::Relaxed), 1);
1270
1271        // writing at a key doesn't notify siblings
1272        *a.clone().label().write() = "Foo".to_string();
1273        tick().await;
1274        assert_eq!(a_count.load(Ordering::Relaxed), 2);
1275        assert_eq!(b_count.load(Ordering::Relaxed), 1);
1276        assert_eq!(c_count.load(Ordering::Relaxed), 1);
1277        let after = store.todos().get_untracked();
1278        assert_eq!(
1279            after.values().cloned().collect::<BTreeSet<_>>(),
1280            BTreeSet::from([
1281                Todo::new(10, "Foo"),
1282                Todo::new(11, "B"),
1283                Todo::new(12, "C"),
1284            ])
1285        );
1286
1287        a.clone().label().set("Bar".into());
1288        let after = store.todos().get_untracked();
1289        assert_eq!(
1290            after.values().cloned().collect::<BTreeSet<_>>(),
1291            BTreeSet::from([
1292                Todo::new(10, "Bar"),
1293                Todo::new(11, "B"),
1294                Todo::new(12, "C")
1295            ]),
1296        );
1297        tick().await;
1298        assert_eq!(a_count.load(Ordering::Relaxed), 3);
1299        assert_eq!(b_count.load(Ordering::Relaxed), 1);
1300        assert_eq!(c_count.load(Ordering::Relaxed), 1);
1301
1302        // we can remove a key and add a new one
1303        store.todos().write().remove(&"C".to_string());
1304        store
1305            .todos()
1306            .write()
1307            .insert("New".to_string(), Todo::new(13, "New"));
1308        let after = store.todos().get_untracked();
1309        assert_eq!(
1310            after.values().cloned().collect::<BTreeSet<_>>(),
1311            BTreeSet::from([
1312                Todo::new(10, "Bar"),
1313                Todo::new(11, "B"),
1314                Todo::new(13, "New"),
1315            ])
1316        );
1317        tick().await;
1318        assert_eq!(a_count.load(Ordering::Relaxed), 4);
1319        assert_eq!(b_count.load(Ordering::Relaxed), 2);
1320        assert_eq!(c_count.load(Ordering::Relaxed), 2);
1321
1322        assert_eq!(
1323            after.keys().cloned().collect::<BTreeSet<String>>(),
1324            BTreeSet::from([
1325                "A".to_string(),
1326                "B".to_string(),
1327                "New".to_string()
1328            ])
1329        );
1330
1331        let at_existing_key = AtKeyed::new(store.todos(), "New".to_string());
1332        let existing = at_existing_key.try_get();
1333        assert!(existing.is_some());
1334        assert_eq!(existing, Some(Todo::new(13, "New")));
1335
1336        let at_faulty_key = AtKeyed::new(store.todos(), "faulty".to_string());
1337        let missing = at_faulty_key.try_get();
1338        assert!(missing.is_none(), "faulty key should return none.")
1339    }
1340
1341    #[test]
1342    fn non_usize_keys_work_for_vec() {
1343        #[derive(Clone, PartialEq, Eq, Hash, Debug)]
1344        struct MyIdType(u32);
1345
1346        #[derive(Debug, Store)]
1347        struct Item {
1348            id: MyIdType,
1349            _value: String,
1350        }
1351
1352        #[derive(Debug, Store)]
1353        struct MyStore {
1354            #[store(key: MyIdType = |item| item.id.clone())]
1355            items: Vec<Item>,
1356        }
1357
1358        let store = Store::new(MyStore { items: Vec::new() });
1359
1360        let _fields = store.items().into_iter();
1361    }
1362
1363    #[tokio::test]
1364    async fn patching_keyed_field_only_notifies_changed_keys() {
1365        _ = any_spawner::Executor::init_tokio();
1366
1367        let store = Store::new(TodoVec::test_data());
1368        assert_eq!(store.read_untracked().todos.len(), 3);
1369
1370        // create an effect to read from each keyed field
1371        let whole_count = Arc::new(AtomicUsize::new(0));
1372        let a_count = Arc::new(AtomicUsize::new(0));
1373        let b_count = Arc::new(AtomicUsize::new(0));
1374        let c_count = Arc::new(AtomicUsize::new(0));
1375
1376        let whole = store.todos();
1377        let a = AtKeyed::new(store.todos(), 10);
1378        let b = AtKeyed::new(store.todos(), 11);
1379        let c = AtKeyed::new(store.todos(), 12);
1380
1381        Effect::new_sync({
1382            let whole_count = Arc::clone(&whole_count);
1383            move || {
1384                whole.track();
1385                whole_count.fetch_add(1, Ordering::Relaxed);
1386            }
1387        });
1388        Effect::new_sync({
1389            let a_count = Arc::clone(&a_count);
1390            move || {
1391                a.track();
1392                a_count.fetch_add(1, Ordering::Relaxed);
1393            }
1394        });
1395        Effect::new_sync({
1396            let b_count = Arc::clone(&b_count);
1397            move || {
1398                b.track();
1399                b_count.fetch_add(1, Ordering::Relaxed);
1400            }
1401        });
1402        Effect::new_sync({
1403            let c_count = Arc::clone(&c_count);
1404            move || {
1405                c.track();
1406                c_count.fetch_add(1, Ordering::Relaxed);
1407            }
1408        });
1409
1410        tick().await;
1411        assert_eq!(whole_count.load(Ordering::Relaxed), 1);
1412        assert_eq!(a_count.load(Ordering::Relaxed), 1);
1413        assert_eq!(b_count.load(Ordering::Relaxed), 1);
1414        assert_eq!(c_count.load(Ordering::Relaxed), 1);
1415
1416        // patching only notifies changed keys
1417        let mut new_data = store.todos().get_untracked();
1418        new_data.swap(0, 2);
1419        store.todos().patch(new_data.clone());
1420        let after = store.todos().get_untracked();
1421        assert_eq!(
1422            after,
1423            vec![Todo::new(12, "C"), Todo::new(11, "B"), Todo::new(10, "A")]
1424        );
1425
1426        tick().await;
1427        assert_eq!(whole_count.load(Ordering::Relaxed), 2);
1428        assert_eq!(a_count.load(Ordering::Relaxed), 1);
1429        assert_eq!(b_count.load(Ordering::Relaxed), 1);
1430        assert_eq!(c_count.load(Ordering::Relaxed), 1);
1431
1432        // and after we move the keys around, they still update the moved items
1433        a.label().set("Bar".into());
1434        let after = store.todos().get_untracked();
1435        assert_eq!(
1436            after,
1437            vec![Todo::new(12, "C"), Todo::new(11, "B"), Todo::new(10, "Bar")]
1438        );
1439        tick().await;
1440        assert_eq!(whole_count.load(Ordering::Relaxed), 3);
1441        assert_eq!(a_count.load(Ordering::Relaxed), 2);
1442        assert_eq!(b_count.load(Ordering::Relaxed), 1);
1443        assert_eq!(c_count.load(Ordering::Relaxed), 1);
1444
1445        // regular writes to the collection notify all keyed children
1446        store.todos().write().pop();
1447        store.todos().write().push(Todo::new(13, "New"));
1448        let after = store.todos().get_untracked();
1449        assert_eq!(
1450            after,
1451            vec![Todo::new(12, "C"), Todo::new(11, "B"), Todo::new(13, "New")]
1452        );
1453        tick().await;
1454        assert_eq!(whole_count.load(Ordering::Relaxed), 4);
1455        assert_eq!(a_count.load(Ordering::Relaxed), 3);
1456        assert_eq!(b_count.load(Ordering::Relaxed), 2);
1457        assert_eq!(c_count.load(Ordering::Relaxed), 2);
1458    }
1459}