perseus/state/rx_collections/
rx_hash_map_nested.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 [`HashMap`] that uses nested reactivity on its
11/// elements. That means the type inside the vector must implement [`MakeRx`]
12/// (usually derived with the `ReactiveState` macro). If you want to store
13/// simple types inside the vector, without nested reactivity (e.g. `String`s),
14/// you should use [`super::RxHashMap`].
15#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
16pub struct RxHashMapNested<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: MakeRx + 'static,
22    V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone;
23/// The reactive version of [`RxHashMapNested`].
24#[derive(Clone, Debug, PartialEq, Eq)]
25pub struct RxHashMapNestedRx<K, V>(RcSignal<HashMap<K, V::Rx>>)
26where
27    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
28    V: MakeRx + Serialize + DeserializeOwned + 'static,
29    V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone;
30
31// --- Reactivity implementations ---
32impl<K, V> MakeRx for RxHashMapNested<K, V>
33where
34    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
35    V: MakeRx + Serialize + DeserializeOwned + 'static,
36    V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone,
37{
38    type Rx = RxHashMapNestedRx<K, V>;
39
40    fn make_rx(self) -> Self::Rx {
41        RxHashMapNestedRx(create_rc_signal(
42            self.0.into_iter().map(|(k, v)| (k, v.make_rx())).collect(),
43        ))
44    }
45}
46impl<K, V> MakeUnrx for RxHashMapNestedRx<K, V>
47where
48    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
49    V: MakeRx + Serialize + DeserializeOwned + 'static,
50    V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone,
51{
52    type Unrx = RxHashMapNested<K, V>;
53
54    fn make_unrx(self) -> Self::Unrx {
55        let map = (*self.0.get_untracked()).clone();
56        RxHashMapNested(map.into_iter().map(|(k, v)| (k, v.make_unrx())).collect())
57    }
58
59    #[cfg(any(client, doc))]
60    fn compute_suspense(&self, cx: Scope) {
61        // We do *not* want to recompute this every time the user changes the state!
62        // (There lie infinite loops.)
63        for elem in self.0.get_untracked().values() {
64            elem.compute_suspense(cx);
65        }
66    }
67}
68// --- Dereferencing ---
69impl<K, V> Deref for RxHashMapNested<K, V>
70where
71    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
72    V: MakeRx + Serialize + DeserializeOwned + 'static,
73    V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone,
74{
75    type Target = HashMap<K, V>;
76
77    fn deref(&self) -> &Self::Target {
78        &self.0
79    }
80}
81impl<K, V> Deref for RxHashMapNestedRx<K, V>
82where
83    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
84    V: MakeRx + Serialize + DeserializeOwned + 'static,
85    V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone,
86{
87    type Target = RcSignal<HashMap<K, V::Rx>>;
88
89    fn deref(&self) -> &Self::Target {
90        &self.0
91    }
92}
93// --- Conversion implementation ---
94impl<K, V> From<HashMap<K, V>> for RxHashMapNested<K, V>
95where
96    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
97    V: MakeRx + Serialize + DeserializeOwned + 'static,
98    V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone,
99{
100    fn from(value: HashMap<K, V>) -> Self {
101        Self(value)
102    }
103}
104
105// --- Freezing implementation ---
106impl<K, V> Freeze for RxHashMapNestedRx<K, V>
107where
108    K: Clone + Serialize + DeserializeOwned + Eq + Hash,
109    V: MakeRx + Serialize + DeserializeOwned + 'static,
110    V::Rx: MakeUnrx<Unrx = V> + Freeze + Clone,
111{
112    fn freeze(&self) -> String {
113        let unrx = Self(self.0.clone()).make_unrx();
114        // This should never panic, because we're dealing with a vector
115        serde_json::to_string(&unrx).unwrap()
116    }
117}