dioxus_stores/impls/
btreemap.rs

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