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