dioxus_stores/impls/
index.rs

1//! Additional utilities for indexing into stores.
2
3use std::{
4    collections::{BTreeMap, HashMap},
5    hash::Hash,
6    ops::{self, Index, IndexMut},
7};
8
9use crate::{scope::SelectorScope, store::Store, ReadStore};
10use dioxus_signals::{
11    AnyStorage, BorrowError, BorrowMutError, ReadSignal, Readable, UnsyncStorage, Writable,
12    WriteLock, WriteSignal,
13};
14
15/// The way a data structure index into its children based on a key. The selector must use this indexing
16/// method consistently to ensure that the same key always maps to the same child.
17pub trait IndexSelector<Idx> {
18    /// Given a selector and an index, scope the selector to the child at the given index.
19    fn scope_selector<Lens>(selector: SelectorScope<Lens>, index: &Idx) -> SelectorScope<Lens>;
20}
21
22impl<T> IndexSelector<usize> for Vec<T> {
23    fn scope_selector<Lens>(selector: SelectorScope<Lens>, index: &usize) -> SelectorScope<Lens> {
24        selector.child_unmapped(*index as _)
25    }
26}
27
28impl<T> IndexSelector<usize> for [T] {
29    fn scope_selector<Lens>(selector: SelectorScope<Lens>, index: &usize) -> SelectorScope<Lens> {
30        selector.child_unmapped(*index as _)
31    }
32}
33
34impl<K, V, I> IndexSelector<I> for HashMap<K, V>
35where
36    I: Hash,
37{
38    fn scope_selector<Lens>(selector: SelectorScope<Lens>, index: &I) -> SelectorScope<Lens> {
39        selector.hash_child_unmapped(&index)
40    }
41}
42
43impl<K, V, I> IndexSelector<I> for BTreeMap<K, V>
44where
45    I: Hash,
46{
47    fn scope_selector<Lens>(selector: SelectorScope<Lens>, index: &I) -> SelectorScope<Lens> {
48        selector.hash_child_unmapped(&index)
49    }
50}
51
52impl<Lens, T> Store<T, Lens> {
53    /// Index into the store, returning a store that allows access to the item at the given index. The
54    /// new store will only update when the item at the index changes.
55    ///
56    /// # Example
57    /// ```rust, no_run
58    /// use dioxus_stores::*;
59    /// let store = use_store(|| vec![1, 2, 3]);
60    /// let indexed_store = store.index(1);
61    /// // The indexed store can access the store methods of the indexed store.
62    /// assert_eq!(indexed_store(), 2);
63    /// ```
64    pub fn index<Idx>(self, index: Idx) -> Store<T::Output, IndexWrite<Idx, Lens>>
65    where
66        T: IndexMut<Idx> + 'static + IndexSelector<Idx>,
67        Lens: Readable<Target = T> + 'static,
68    {
69        T::scope_selector(self.into_selector(), &index)
70            .map_writer(move |write| IndexWrite { index, write })
71            .into()
72    }
73}
74
75/// A specific index in a `Readable` / `Writable` type
76#[derive(Clone, Copy)]
77pub struct IndexWrite<Index, Write> {
78    index: Index,
79    write: Write,
80}
81
82impl<Index, Write> Readable for IndexWrite<Index, Write>
83where
84    Write: Readable,
85    Write::Target: ops::Index<Index> + 'static,
86    Index: Clone,
87{
88    type Target = <Write::Target as ops::Index<Index>>::Output;
89
90    type Storage = Write::Storage;
91
92    fn try_read_unchecked(&self) -> Result<dioxus_signals::ReadableRef<'static, Self>, BorrowError>
93    where
94        Self::Target: 'static,
95    {
96        self.write.try_read_unchecked().map(|value| {
97            Self::Storage::map(value, |value: &Write::Target| {
98                value.index(self.index.clone())
99            })
100        })
101    }
102
103    fn try_peek_unchecked(&self) -> Result<dioxus_signals::ReadableRef<'static, Self>, BorrowError>
104    where
105        Self::Target: 'static,
106    {
107        self.write.try_peek_unchecked().map(|value| {
108            Self::Storage::map(value, |value: &Write::Target| {
109                value.index(self.index.clone())
110            })
111        })
112    }
113
114    fn subscribers(&self) -> dioxus_core::Subscribers
115    where
116        Self::Target: 'static,
117    {
118        self.write.subscribers()
119    }
120}
121
122impl<Index, Write> Writable for IndexWrite<Index, Write>
123where
124    Write: Writable,
125    Write::Target: ops::IndexMut<Index> + 'static,
126    Index: Clone,
127{
128    type WriteMetadata = Write::WriteMetadata;
129
130    fn try_write_unchecked(
131        &self,
132    ) -> Result<dioxus_signals::WritableRef<'static, Self>, BorrowMutError>
133    where
134        Self::Target: 'static,
135    {
136        self.write.try_write_unchecked().map(|value| {
137            WriteLock::map(value, |value: &mut Write::Target| {
138                value.index_mut(self.index.clone())
139            })
140        })
141    }
142}
143
144impl<Idx, T, Write> ::std::convert::From<Store<T, IndexWrite<Idx, Write>>>
145    for Store<T, WriteSignal<T>>
146where
147    Write: Writable<Storage = UnsyncStorage> + 'static,
148    Write::WriteMetadata: 'static,
149    Write::Target: ops::IndexMut<Idx, Output = T> + 'static,
150    Idx: Clone + 'static,
151    T: 'static,
152{
153    fn from(value: Store<T, IndexWrite<Idx, Write>>) -> Self {
154        value
155            .into_selector()
156            .map_writer(|writer| WriteSignal::new(writer))
157            .into()
158    }
159}
160
161impl<Idx, T, Write> ::std::convert::From<Store<T, IndexWrite<Idx, Write>>> for ReadStore<T>
162where
163    Write: Readable<Storage = UnsyncStorage> + 'static,
164    Write::Target: ops::Index<Idx, Output = T> + 'static,
165    Idx: Clone + 'static,
166    T: 'static,
167{
168    fn from(value: Store<T, IndexWrite<Idx, Write>>) -> Self {
169        value
170            .into_selector()
171            .map_writer(|writer| ReadSignal::new(writer))
172            .into()
173    }
174}