perseus/state/rx_collections/
rx_hash_map.rs

1use crate::state::{Freeze, MakeRx, MakeUnrx};
2use serde::{de::DeserializeOwned, Deserialize, Serialize};
3use std::collections::HashMap;
4use std::hash::Hash;
5use std::ops::Deref;
6#[cfg(any(client, doc))]
7use sycamore::prelude::Scope;
8use sycamore::reactive::{create_rc_signal, RcSignal};
9
10/// A reactive version of [`Vec`] that uses nested reactivity on its elements.
11/// This requires nothing by `Clone + 'static` of the elements inside the map,
12/// and it wraps them in `RcSignal`s to make them reactive. If you want to store
13/// nested reactive types inside the map (e.g. `String`s), you should
14/// use [`super::RxHashMapNested`].
15#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
16pub struct RxHashMap<K, V>(HashMap<K, V>)
17where
18    K: Clone + Eq + Hash,
19    // We get the `Deserialize` derive macro working by tricking Serde by not
20    // including the actual bounds here
21    V: Clone + 'static;
22/// The reactive version of [`RxHashMap`].
23#[derive(Clone, Debug, PartialEq, Eq)]
24pub struct RxHashMapRx<K, V>(RcSignal<HashMap<K, RcSignal<V>>>)
25where
26    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
27    V: Clone + Serialize + DeserializeOwned + 'static;
28
29// --- Reactivity implementations ---
30impl<K, V> MakeRx for RxHashMap<K, V>
31where
32    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
33    V: Clone + Serialize + DeserializeOwned + 'static,
34{
35    type Rx = RxHashMapRx<K, V>;
36
37    fn make_rx(self) -> Self::Rx {
38        RxHashMapRx(create_rc_signal(
39            self.0
40                .into_iter()
41                .map(|(k, v)| (k, create_rc_signal(v)))
42                .collect(),
43        ))
44    }
45}
46impl<K, V> MakeUnrx for RxHashMapRx<K, V>
47where
48    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
49    V: Clone + Serialize + DeserializeOwned + 'static,
50{
51    type Unrx = RxHashMap<K, V>;
52
53    fn make_unrx(self) -> Self::Unrx {
54        let map = (*self.0.get_untracked()).clone();
55        RxHashMap(
56            map.into_iter()
57                .map(|(k, v)| (k, (*v.get_untracked()).clone()))
58                .collect(),
59        )
60    }
61
62    #[cfg(any(client, doc))]
63    fn compute_suspense(&self, _cx: Scope) {}
64}
65// --- Dereferencing ---
66impl<K, V> Deref for RxHashMap<K, V>
67where
68    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
69    V: Clone + Serialize + DeserializeOwned + 'static,
70{
71    type Target = HashMap<K, V>;
72
73    fn deref(&self) -> &Self::Target {
74        &self.0
75    }
76}
77impl<K, V> Deref for RxHashMapRx<K, V>
78where
79    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
80    V: Clone + Serialize + DeserializeOwned + 'static,
81{
82    type Target = RcSignal<HashMap<K, RcSignal<V>>>;
83
84    fn deref(&self) -> &Self::Target {
85        &self.0
86    }
87}
88// --- Conversion implementation ---
89impl<K, V> From<HashMap<K, V>> for RxHashMap<K, V>
90where
91    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
92    V: Clone + Serialize + DeserializeOwned + 'static,
93{
94    fn from(value: HashMap<K, V>) -> Self {
95        Self(value)
96    }
97}
98
99// --- Freezing implementation ---
100impl<K, V> Freeze for RxHashMapRx<K, V>
101where
102    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
103    V: Clone + Serialize + DeserializeOwned + 'static,
104{
105    fn freeze(&self) -> String {
106        let unrx = Self(self.0.clone()).make_unrx();
107        // This should never panic, because we're dealing with a vector
108        serde_json::to_string(&unrx).unwrap()
109    }
110}