nami_core/
dictionary.rs

1//! A module providing a reactive dictionary data structure.
2//! This module defines the `Dictionary` trait and a reactive `Map`
3//! implementation that allows watching for changes to key-value pairs.
4
5use core::cell::RefCell;
6
7use crate::watcher::{Context, WatcherGuard, WatcherManager};
8use alloc::{collections::btree_map::BTreeMap, rc::Rc};
9
10/// A trait for dictionary-like data structures that support reactive watching of key-value pairs.
11pub trait Dictionary {
12    /// The type of keys in the dictionary.
13    type Key: 'static;
14    /// The type of values in the dictionary.
15    type Value: 'static;
16    /// The type of guard returned when registering a watcher.
17    type Guard: WatcherGuard;
18
19    /// Gets a value from the dictionary for the specified key.
20    fn get(&self, key: &Self::Key) -> Option<Self::Value>;
21    /// Registers a watcher for changes to the specified key in the dictionary.
22    fn watch(
23        &self,
24        key: &Self::Key,
25        watcher: impl Fn(Context<Option<Self::Value>>) + 'static,
26    ) -> Self::Guard;
27}
28
29impl<K, V> Dictionary for BTreeMap<K, V>
30where
31    K: Ord + Clone + 'static,
32    V: Clone + 'static,
33{
34    type Key = K;
35    type Value = V;
36    type Guard = ();
37
38    fn get(&self, key: &Self::Key) -> Option<Self::Value> {
39        self.get(key).cloned()
40    }
41
42    fn watch(
43        &self,
44        _key: &Self::Key,
45        _watcher: impl Fn(Context<Option<Self::Value>>) + 'static,
46    ) -> Self::Guard {
47        // BTreeMap is static - no reactivity, so watch is a no-op
48    }
49}
50
51/// A reactive dictionary that allows watching for changes to its key-value pairs.
52#[derive(Debug)]
53pub struct Map<K, V> {
54    map: Rc<RefCell<BTreeMap<K, MapValue<V>>>>,
55}
56
57impl<K, V> Clone for Map<K, V> {
58    fn clone(&self) -> Self {
59        Self {
60            map: Rc::clone(&self.map),
61        }
62    }
63}
64
65#[derive(Debug)]
66struct MapValue<V> {
67    value: Option<V>,
68    watchers: WatcherManager<Option<V>>,
69}
70
71impl<K: Ord + Clone + 'static, V: Clone + 'static> Dictionary for Map<K, V> {
72    type Key = K;
73    type Value = V;
74    type Guard = crate::watcher::WatcherManagerGuard<Option<V>>;
75
76    fn get(&self, key: &Self::Key) -> Option<Self::Value> {
77        let map = self.map.borrow();
78        map.get(key).and_then(|mv| mv.value.clone())
79    }
80
81    fn watch(
82        &self,
83        key: &Self::Key,
84        watcher: impl Fn(Context<Option<Self::Value>>) + 'static,
85    ) -> Self::Guard {
86        let mut map = self.map.borrow_mut();
87        let mv = map.entry(key.clone()).or_insert_with(|| MapValue {
88            value: None,
89            watchers: WatcherManager::new(),
90        });
91        mv.watchers.register_as_guard(watcher)
92    }
93}
94
95#[cfg(feature = "std")]
96mod std_impls {
97    extern crate std;
98    use super::{Context, Dictionary};
99    use std::collections::HashMap;
100    use std::hash::{BuildHasher, Hash};
101
102    impl<K: Hash + Eq + Clone + 'static, V: Clone + 'static, S: BuildHasher + 'static> Dictionary
103        for HashMap<K, V, S>
104    {
105        type Key = K;
106        type Value = V;
107        type Guard = ();
108
109        fn get(&self, key: &Self::Key) -> Option<Self::Value> {
110            self.get(key).cloned()
111        }
112
113        fn watch(
114            &self,
115            _key: &Self::Key,
116            _watcher: impl Fn(Context<Option<Self::Value>>) + 'static,
117        ) -> Self::Guard {
118            // HashMap is static - no reactivity, so watch is a no-op
119        }
120    }
121}