//! Additional utilities for `BTreeMap` stores. use std::{borrow::Borrow, collections::BTreeMap, hash::Hash, iter::FusedIterator}; use crate::{store::Store, ReadStore}; use dioxus_signals::{ AnyStorage, BorrowError, BorrowMutError, ReadSignal, Readable, ReadableExt, UnsyncStorage, Writable, WriteLock, WriteSignal, }; impl> + 'static, K: 'static, V: 'static> Store, Lens> { /// Get the length of the BTreeMap. This method will track the store shallowly and only cause /// re-runs when items are added or removed from the map, not when existing values are modified. /// /// # Example /// ```rust, no_run /// use dioxus_stores::*; /// use dioxus::prelude::*; /// use std::collections::BTreeMap; /// let mut store = use_store(|| BTreeMap::new()); /// assert_eq!(store.len(), 0); /// store.insert(0, "value".to_string()); /// assert_eq!(store.len(), 1); /// ``` pub fn len(&self) -> usize { self.selector().track_shallow(); self.selector().peek().len() } /// Check if the BTreeMap is empty. This method will track the store shallowly and only cause /// re-runs when items are added or removed from the map, not when existing values are modified. /// /// # Example /// ```rust, no_run /// use dioxus_stores::*; /// use dioxus::prelude::*; /// use std::collections::BTreeMap; /// let mut store = use_store(|| BTreeMap::new()); /// assert!(store.is_empty()); /// store.insert(0, "value".to_string()); /// assert!(!store.is_empty()); /// ``` pub fn is_empty(&self) -> bool { self.selector().track_shallow(); self.selector().peek().is_empty() } /// Iterate over the current entries in the BTreeMap, returning a tuple of the key and a store for the value. This method /// will track the store shallowly and only cause re-runs when items are added or removed from the map, not when existing /// values are modified. /// /// # Example /// /// ```rust, no_run /// use dioxus_stores::*; /// use dioxus::prelude::*; /// use std::collections::BTreeMap; /// let mut store = use_store(|| BTreeMap::new()); /// store.insert(0, "value1".to_string()); /// store.insert(1, "value2".to_string()); /// for (key, value_store) in store.iter() { /// println!("{}: {}", key, value_store.read()); /// } /// ``` pub fn iter( &self, ) -> impl ExactSizeIterator>)> + DoubleEndedIterator + FusedIterator + '_ where K: Hash + Ord + Clone, Lens: Clone, { self.selector().track_shallow(); let keys: Vec<_> = self.selector().peek_unchecked().keys().cloned().collect(); keys.into_iter().map(move |key| { let value = self.clone().get(key.clone()).unwrap(); (key, value) }) } /// Get an iterator over the values in the BTreeMap. This method will track the store shallowly and only cause /// re-runs when items are added or removed from the map, not when existing values are modified. /// /// # Example /// ```rust, no_run /// use dioxus_stores::*; /// use dioxus::prelude::*; /// use std::collections::BTreeMap; /// let mut store = use_store(|| BTreeMap::new()); /// store.insert(0, "value1".to_string()); /// store.insert(1, "value2".to_string()); /// for value_store in store.values() { /// println!("{}", value_store.read()); /// } /// ``` pub fn values( &self, ) -> impl ExactSizeIterator>> + DoubleEndedIterator + FusedIterator + '_ where K: Hash + Ord + Clone, Lens: Clone, { self.selector().track_shallow(); let keys = self.selector().peek().keys().cloned().collect::>(); keys.into_iter() .map(move |key| self.clone().get(key).unwrap()) } /// Insert a new key-value pair into the BTreeMap. This method will mark the store as shallowly dirty, causing /// re-runs of any reactive scopes that depend on the shape of the map. /// /// # Example /// ```rust, no_run /// use dioxus_stores::*; /// use dioxus::prelude::*; /// use std::collections::BTreeMap; /// let mut store = use_store(|| BTreeMap::new()); /// assert!(store.get(0).is_none()); /// store.insert(0, "value".to_string()); /// assert_eq!(store.get(0).unwrap().cloned(), "value".to_string()); /// ``` pub fn insert(&mut self, key: K, value: V) where K: Ord, Lens: Writable, { self.selector().mark_dirty_shallow(); self.selector().write_untracked().insert(key, value); } /// Remove a key-value pair from the BTreeMap. This method will mark the store as shallowly dirty, causing /// re-runs of any reactive scopes that depend on the shape of the map or the value of the removed key. /// /// # Example /// ```rust, no_run /// use dioxus_stores::*; /// use dioxus::prelude::*; /// use std::collections::BTreeMap; /// let mut store = use_store(|| BTreeMap::new()); /// store.insert(0, "value".to_string()); /// assert_eq!(store.get(0).unwrap().cloned(), "value".to_string()); /// let removed_value = store.remove(&0); /// assert_eq!(removed_value, Some("value".to_string())); /// assert!(store.get(0).is_none()); /// ``` pub fn remove(&mut self, key: &Q) -> Option where Q: ?Sized + Ord + 'static, K: Borrow + Ord, Lens: Writable, { self.selector().mark_dirty_shallow(); self.selector().write_untracked().remove(key) } /// Clear the BTreeMap, removing all key-value pairs. This method will mark the store as shallowly dirty, /// causing re-runs of any reactive scopes that depend on the shape of the map. /// /// # Example /// ```rust, no_run /// use dioxus_stores::*; /// use dioxus::prelude::*; /// use std::collections::BTreeMap; /// let mut store = use_store(|| BTreeMap::new()); /// store.insert(1, "value1".to_string()); /// store.insert(2, "value2".to_string()); /// assert_eq!(store.len(), 2); /// store.clear(); /// assert!(store.is_empty()); /// ``` pub fn clear(&mut self) where Lens: Writable, { self.selector().mark_dirty_shallow(); self.selector().write_untracked().clear(); } /// Retain only the key-value pairs that satisfy the given predicate. This method will mark the store as shallowly dirty, /// causing re-runs of any reactive scopes that depend on the shape of the map or the values retained. /// /// # Example /// ```rust, no_run /// use dioxus_stores::*; /// use dioxus::prelude::*; /// use std::collections::BTreeMap; /// let mut store = use_store(|| BTreeMap::new()); /// store.insert(1, "value1".to_string()); /// store.insert(2, "value2".to_string()); /// store.retain(|key, value| *key == 1); /// assert_eq!(store.len(), 1); /// assert!(store.get(1).is_some()); /// assert!(store.get(2).is_none()); /// ``` pub fn retain(&mut self, mut f: impl FnMut(&K, &V) -> bool) where Lens: Writable, K: Ord, { self.selector().mark_dirty_shallow(); self.selector().write_untracked().retain(|k, v| f(k, v)); } /// Check if the BTreeMap contains a key. This method will track the store shallowly and only cause /// re-runs when items are added or removed from the map, not when existing values are modified. /// /// # Example /// ```rust, no_run /// use dioxus_stores::*; /// use dioxus::prelude::*; /// use std::collections::BTreeMap; /// let mut store = use_store(|| BTreeMap::new()); /// assert!(!store.contains_key(&0)); /// store.insert(0, "value".to_string()); /// assert!(store.contains_key(&0)); /// ``` pub fn contains_key(&self, key: &Q) -> bool where Q: ?Sized + Ord + 'static, K: Borrow + Ord, { self.selector().track_shallow(); self.selector().peek().contains_key(key) } /// Get a store for the value associated with the given key. This method creates a new store scope /// that tracks just changes to the value associated with the key. /// /// # Example /// ```rust, no_run /// use dioxus_stores::*; /// use dioxus::prelude::*; /// use std::collections::BTreeMap; /// let mut store = use_store(|| BTreeMap::new()); /// assert!(store.get(0).is_none()); /// store.insert(0, "value".to_string()); /// assert_eq!(store.get(0).unwrap().cloned(), "value".to_string()); /// ``` pub fn get(self, key: Q) -> Option>> where Q: Hash + Ord + 'static, K: Borrow + Ord, { self.contains_key(&key).then(|| { self.into_selector() .hash_child_unmapped(key.borrow()) .map_writer(move |writer| GetWrite { index: key, write: writer, }) .into() }) } } /// A specific index in a `Readable` / `Writable` BTreeMap #[derive(Clone, Copy)] pub struct GetWrite { index: Index, write: Write, } impl Readable for GetWrite where Write: Readable>, Index: Ord + 'static, K: Borrow + Ord + 'static, { type Target = V; type Storage = Write::Storage; fn try_read_unchecked(&self) -> Result, BorrowError> where Self::Target: 'static, { self.write.try_read_unchecked().map(|value| { Self::Storage::map(value, |value: &Write::Target| { value .get(&self.index) .expect("Tried to access a key that does not exist") }) }) } fn try_peek_unchecked(&self) -> Result, BorrowError> where Self::Target: 'static, { self.write.try_peek_unchecked().map(|value| { Self::Storage::map(value, |value: &Write::Target| { value .get(&self.index) .expect("Tried to access a key that does not exist") }) }) } fn subscribers(&self) -> dioxus_core::Subscribers where Self::Target: 'static, { self.write.subscribers() } } impl Writable for GetWrite where Write: Writable>, Index: Ord + 'static, K: Borrow + Ord + 'static, { type WriteMetadata = Write::WriteMetadata; fn try_write_unchecked( &self, ) -> Result, BorrowMutError> where Self::Target: 'static, { self.write.try_write_unchecked().map(|value| { WriteLock::map(value, |value: &mut Write::Target| { value .get_mut(&self.index) .expect("Tried to access a key that does not exist") }) }) } } impl ::std::convert::From>> for Store> where Write::WriteMetadata: 'static, Write: Writable, Storage = UnsyncStorage> + 'static, Index: Ord + 'static, K: Borrow + Ord + 'static, V: 'static, { fn from(value: Store>) -> Self { value .into_selector() .map_writer(|writer| WriteSignal::new(writer)) .into() } } impl ::std::convert::From>> for ReadStore where Write: Readable, Storage = UnsyncStorage> + 'static, Index: Ord + 'static, K: Borrow + Ord + 'static, V: 'static, { fn from(value: Store>) -> Self { value .into_selector() .map_writer(|writer| ReadSignal::new(writer)) .into() } }