dioxus_stores/impls/
hashmap.rs

1//! Additional utilities for `HashMap` stores.
2
3use std::{
4    borrow::Borrow,
5    collections::HashMap,
6    hash::{BuildHasher, Hash},
7    iter::FusedIterator,
8};
9
10use crate::{store::Store, ReadStore};
11use dioxus_signals::{
12    AnyStorage, BorrowError, BorrowMutError, ReadSignal, Readable, ReadableExt, UnsyncStorage,
13    Writable, WriteLock, WriteSignal,
14};
15
16impl<Lens: Readable<Target = HashMap<K, V, St>> + 'static, K: 'static, V: 'static, St: 'static>
17    Store<HashMap<K, V, St>, Lens>
18{
19    /// Get the length of the HashMap. This method will track the store shallowly and only cause
20    /// re-runs when items are added or removed from the map, not when existing values are modified.
21    ///
22    /// # Example
23    /// ```rust, no_run
24    /// use dioxus_stores::*;
25    /// use dioxus::prelude::*;
26    /// use std::collections::HashMap;
27    /// let mut store = use_store(|| HashMap::new());
28    /// assert_eq!(store.len(), 0);
29    /// store.insert(0, "value".to_string());
30    /// assert_eq!(store.len(), 1);
31    /// ```
32    pub fn len(&self) -> usize {
33        self.selector().track_shallow();
34        self.selector().peek().len()
35    }
36
37    /// Check if the HashMap is empty. This method will track the store shallowly and only cause
38    /// re-runs when items are added or removed from the map, not when existing values are modified.
39    ///
40    /// # Example
41    /// ```rust, no_run
42    /// use dioxus_stores::*;
43    /// use dioxus::prelude::*;
44    /// use std::collections::HashMap;
45    /// let mut store = use_store(|| HashMap::new());
46    /// assert!(store.is_empty());
47    /// store.insert(0, "value".to_string());
48    /// assert!(!store.is_empty());
49    /// ```
50    pub fn is_empty(&self) -> bool {
51        self.selector().track_shallow();
52        self.selector().peek().is_empty()
53    }
54
55    /// Iterate over the current entries in the HashMap, returning a tuple of the key and a store for the value. This method
56    /// will track the store shallowly and only cause re-runs when items are added or removed from the map, not when existing
57    /// values are modified.
58    ///
59    /// # Example
60    ///
61    /// ```rust, no_run
62    /// use dioxus_stores::*;
63    /// use dioxus::prelude::*;
64    /// use std::collections::HashMap;
65    /// let mut store = use_store(|| HashMap::new());
66    /// store.insert(0, "value1".to_string());
67    /// store.insert(1, "value2".to_string());
68    /// for (key, value_store) in store.iter() {
69    ///     println!("{}: {}", key, value_store.read());
70    /// }
71    /// ```
72    pub fn iter(
73        &self,
74    ) -> impl ExactSizeIterator<Item = (K, Store<V, GetWrite<K, Lens>>)>
75           + DoubleEndedIterator
76           + FusedIterator
77           + '_
78    where
79        K: Eq + Hash + Clone,
80        St: BuildHasher,
81        Lens: Clone,
82    {
83        self.selector().track_shallow();
84        let keys: Vec<_> = self.selector().peek_unchecked().keys().cloned().collect();
85        keys.into_iter().map(move |key| {
86            let value = self.clone().get(key.clone()).unwrap();
87            (key, value)
88        })
89    }
90
91    /// Get an iterator over the values in the HashMap. This method will track the store shallowly and only cause
92    /// re-runs when items are added or removed from the map, not when existing values are modified.
93    ///
94    /// # Example
95    /// ```rust, no_run
96    /// use dioxus_stores::*;
97    /// use dioxus::prelude::*;
98    /// use std::collections::HashMap;
99    /// let mut store = use_store(|| HashMap::new());
100    /// store.insert(0, "value1".to_string());
101    /// store.insert(1, "value2".to_string());
102    /// for value_store in store.values() {
103    ///     println!("{}", value_store.read());
104    /// }
105    /// ```
106    pub fn values(
107        &self,
108    ) -> impl ExactSizeIterator<Item = Store<V, GetWrite<K, Lens>>>
109           + DoubleEndedIterator
110           + FusedIterator
111           + '_
112    where
113        K: Eq + Hash + Clone,
114        St: BuildHasher,
115        Lens: Clone,
116    {
117        self.selector().track_shallow();
118        let keys = self.selector().peek().keys().cloned().collect::<Vec<_>>();
119        keys.into_iter()
120            .map(move |key| self.clone().get(key).unwrap())
121    }
122
123    /// Insert a new key-value pair into the HashMap. This method will mark the store as shallowly dirty, causing
124    /// re-runs of any reactive scopes that depend on the shape of the map.
125    ///
126    /// # Example
127    /// ```rust, no_run
128    /// use dioxus_stores::*;
129    /// use dioxus::prelude::*;
130    /// use std::collections::HashMap;
131    /// let mut store = use_store(|| HashMap::new());
132    /// assert!(store.get(0).is_none());
133    /// store.insert(0, "value".to_string());
134    /// assert_eq!(store.get(0).unwrap().cloned(), "value".to_string());
135    /// ```
136    pub fn insert(&mut self, key: K, value: V)
137    where
138        K: Eq + Hash,
139        St: BuildHasher,
140        Lens: Writable,
141    {
142        self.selector().mark_dirty_shallow();
143        self.selector().write_untracked().insert(key, value);
144    }
145
146    /// Remove a key-value pair from the HashMap. This method will mark the store as shallowly dirty, causing
147    /// re-runs of any reactive scopes that depend on the shape of the map or the value of the removed key.
148    ///
149    /// # Example
150    /// ```rust, no_run
151    /// use dioxus_stores::*;
152    /// use dioxus::prelude::*;
153    /// use std::collections::HashMap;
154    /// let mut store = use_store(|| HashMap::new());
155    /// store.insert(0, "value".to_string());
156    /// assert_eq!(store.get(0).unwrap().cloned(), "value".to_string());
157    /// let removed_value = store.remove(&0);
158    /// assert_eq!(removed_value, Some("value".to_string()));
159    /// assert!(store.get(0).is_none());
160    /// ```
161    pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
162    where
163        Q: ?Sized + Hash + Eq + 'static,
164        K: Borrow<Q> + Eq + Hash,
165        St: BuildHasher,
166        Lens: Writable,
167    {
168        self.selector().mark_dirty_shallow();
169        self.selector().write_untracked().remove(key)
170    }
171
172    /// Clear the HashMap, removing all key-value pairs. This method will mark the store as shallowly dirty,
173    /// causing re-runs of any reactive scopes that depend on the shape of the map.
174    ///
175    /// # Example
176    /// ```rust, no_run
177    /// use dioxus_stores::*;
178    /// use dioxus::prelude::*;
179    /// use std::collections::HashMap;
180    /// let mut store = use_store(|| HashMap::new());
181    /// store.insert(1, "value1".to_string());
182    /// store.insert(2, "value2".to_string());
183    /// assert_eq!(store.len(), 2);
184    /// store.clear();
185    /// assert!(store.is_empty());
186    /// ```
187    pub fn clear(&mut self)
188    where
189        Lens: Writable,
190    {
191        self.selector().mark_dirty_shallow();
192        self.selector().write_untracked().clear();
193    }
194
195    /// Retain only the key-value pairs that satisfy the given predicate. This method will mark the store as shallowly dirty,
196    /// causing re-runs of any reactive scopes that depend on the shape of the map or the values retained.
197    ///
198    /// # Example
199    /// ```rust, no_run
200    /// use dioxus_stores::*;
201    /// use dioxus::prelude::*;
202    /// use std::collections::HashMap;
203    /// let mut store = use_store(|| HashMap::new());
204    /// store.insert(1, "value1".to_string());
205    /// store.insert(2, "value2".to_string());
206    /// store.retain(|key, value| *key == 1);
207    /// assert_eq!(store.len(), 1);
208    /// assert!(store.get(1).is_some());
209    /// assert!(store.get(2).is_none());
210    /// ```
211    pub fn retain(&mut self, mut f: impl FnMut(&K, &V) -> bool)
212    where
213        Lens: Writable,
214    {
215        self.selector().mark_dirty_shallow();
216        self.selector().write_untracked().retain(|k, v| f(k, v));
217    }
218
219    /// Check if the HashMap contains a key. This method will track the store shallowly and only cause
220    /// re-runs when items are added or removed from the map, not when existing values are modified.
221    ///
222    /// # Example
223    /// ```rust, no_run
224    /// use dioxus_stores::*;
225    /// use dioxus::prelude::*;
226    /// use std::collections::HashMap;
227    /// let mut store = use_store(|| HashMap::new());
228    /// assert!(!store.contains_key(&0));
229    /// store.insert(0, "value".to_string());
230    /// assert!(store.contains_key(&0));
231    /// ```
232    pub fn contains_key<Q>(&self, key: &Q) -> bool
233    where
234        Q: ?Sized + Hash + Eq + 'static,
235        K: Borrow<Q> + Eq + Hash,
236        St: BuildHasher,
237    {
238        self.selector().track_shallow();
239        self.selector().peek().contains_key(key)
240    }
241
242    /// Get a store for the value associated with the given key. This method creates a new store scope
243    /// that tracks just changes to the value associated with the key.
244    ///
245    /// # Example
246    /// ```rust, no_run
247    /// use dioxus_stores::*;
248    /// use dioxus::prelude::*;
249    /// use std::collections::HashMap;
250    /// let mut store = use_store(|| HashMap::new());
251    /// assert!(store.get(0).is_none());
252    /// store.insert(0, "value".to_string());
253    /// assert_eq!(store.get(0).unwrap().cloned(), "value".to_string());
254    /// ```
255    pub fn get<Q>(self, key: Q) -> Option<Store<V, GetWrite<Q, Lens>>>
256    where
257        Q: Hash + Eq + 'static,
258        K: Borrow<Q> + Eq + Hash,
259        St: BuildHasher,
260    {
261        self.contains_key(&key).then(|| {
262            self.into_selector()
263                .hash_child_unmapped(key.borrow())
264                .map_writer(move |writer| GetWrite {
265                    index: key,
266                    write: writer,
267                })
268                .into()
269        })
270    }
271}
272
273/// A specific index in a `Readable` / `Writable` hashmap
274#[derive(Clone, Copy)]
275pub struct GetWrite<Index, Write> {
276    index: Index,
277    write: Write,
278}
279
280impl<Index, Write, K, V, St> Readable for GetWrite<Index, Write>
281where
282    Write: Readable<Target = HashMap<K, V, St>>,
283    Index: Hash + Eq + 'static,
284    K: Borrow<Index> + Eq + Hash + 'static,
285    St: BuildHasher + 'static,
286{
287    type Target = V;
288
289    type Storage = Write::Storage;
290
291    fn try_read_unchecked(&self) -> Result<dioxus_signals::ReadableRef<'static, Self>, BorrowError>
292    where
293        Self::Target: 'static,
294    {
295        self.write.try_read_unchecked().map(|value| {
296            Self::Storage::map(value, |value: &Write::Target| {
297                value
298                    .get(&self.index)
299                    .expect("Tried to access a key that does not exist")
300            })
301        })
302    }
303
304    fn try_peek_unchecked(&self) -> Result<dioxus_signals::ReadableRef<'static, Self>, BorrowError>
305    where
306        Self::Target: 'static,
307    {
308        self.write.try_peek_unchecked().map(|value| {
309            Self::Storage::map(value, |value: &Write::Target| {
310                value
311                    .get(&self.index)
312                    .expect("Tried to access a key that does not exist")
313            })
314        })
315    }
316
317    fn subscribers(&self) -> dioxus_core::Subscribers
318    where
319        Self::Target: 'static,
320    {
321        self.write.subscribers()
322    }
323}
324
325impl<Index, Write, K, V, St> Writable for GetWrite<Index, Write>
326where
327    Write: Writable<Target = HashMap<K, V, St>>,
328    Index: Hash + Eq + 'static,
329    K: Borrow<Index> + Eq + Hash + 'static,
330    St: BuildHasher + 'static,
331{
332    type WriteMetadata = Write::WriteMetadata;
333
334    fn try_write_unchecked(
335        &self,
336    ) -> Result<dioxus_signals::WritableRef<'static, Self>, BorrowMutError>
337    where
338        Self::Target: 'static,
339    {
340        self.write.try_write_unchecked().map(|value| {
341            WriteLock::map(value, |value: &mut Write::Target| {
342                value
343                    .get_mut(&self.index)
344                    .expect("Tried to access a key that does not exist")
345            })
346        })
347    }
348}
349
350impl<Index, Write, K, V, St> ::std::convert::From<Store<V, GetWrite<Index, Write>>>
351    for Store<V, WriteSignal<V>>
352where
353    Write::WriteMetadata: 'static,
354    Write: Writable<Target = HashMap<K, V, St>, Storage = UnsyncStorage> + 'static,
355    Index: Hash + Eq + 'static,
356    K: Borrow<Index> + Eq + Hash + 'static,
357    St: BuildHasher + 'static,
358    V: 'static,
359{
360    fn from(value: Store<V, GetWrite<Index, Write>>) -> Self {
361        value
362            .into_selector()
363            .map_writer(|writer| WriteSignal::new(writer))
364            .into()
365    }
366}
367
368impl<Index, Write, K, V, St> ::std::convert::From<Store<V, GetWrite<Index, Write>>> for ReadStore<V>
369where
370    Write: Readable<Target = HashMap<K, V, St>, Storage = UnsyncStorage> + 'static,
371    Index: Hash + Eq + 'static,
372    K: Borrow<Index> + Eq + Hash + 'static,
373    St: BuildHasher + 'static,
374    V: 'static,
375{
376    fn from(value: Store<V, GetWrite<Index, Write>>) -> Self {
377        value
378            .into_selector()
379            .map_writer(|writer| ReadSignal::new(writer))
380            .into()
381    }
382}