amethystate_core/primitives/
map_core.rs1use crate::SignalSubscription;
2use crate::change::MapChange;
3use crate::primitives::intercept::{InterceptDisposer, InterceptGuard};
4use crate::primitives::signal::SubscriptionMeta;
5use serde::Serialize;
6use serde::de::DeserializeOwned;
7use std::collections::HashMap;
8use std::fmt::Display;
9use std::hash::Hash;
10use std::str::FromStr;
11use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
12use std::sync::{Arc, Mutex};
13
14pub type InterceptorAny<K, V> =
15 Arc<dyn Fn(MapChange<K, V>) -> Option<MapChange<K, V>> + Send + Sync + 'static>;
16pub type InterceptorKey<K, V> =
17 Arc<dyn Fn(MapChange<K, V>) -> Option<MapChange<K, V>> + Send + Sync + 'static>;
18pub type SubscriberAny<K, V> = Arc<dyn Fn(&MapChange<K, V>) + Send + Sync + 'static>;
19pub type SubscriberKey<K, V> = Arc<dyn Fn(&MapChange<K, V>) + Send + Sync + 'static>;
20
21pub trait ReactiveMapKey: FromStr + Display + Clone + Hash + Eq + Send + Sync + 'static {}
22impl<T: FromStr + Display + Clone + Hash + Eq + Send + Sync + 'static> ReactiveMapKey for T {}
23
24pub trait ReactiveMapValue:
25 Serialize + DeserializeOwned + Clone + Send + Sync + 'static + Default
26{
27}
28impl<T: Serialize + DeserializeOwned + Clone + Send + Sync + 'static + Default> ReactiveMapValue
29 for T
30{
31}
32
33pub struct ReactiveMapCore<K, V> {
34 pub interceptors_any: Arc<Mutex<Vec<(u64, InterceptorAny<K, V>)>>>,
35 pub interceptors_key: Arc<Mutex<HashMap<K, Vec<(u64, InterceptorKey<K, V>)>>>>,
36 pub subscribers_any: Arc<Mutex<Vec<(u64, SubscriberAny<K, V>, SubscriptionMeta)>>>,
37 pub subscribers_key: Arc<Mutex<HashMap<K, Vec<(u64, SubscriberKey<K, V>, SubscriptionMeta)>>>>,
38 pub next_id: Arc<AtomicU64>,
39 pub intercept_depth: Arc<AtomicUsize>,
40 pub cache: Arc<Mutex<HashMap<K, V>>>,
41}
42
43impl<K, V> Clone for ReactiveMapCore<K, V> {
44 fn clone(&self) -> Self {
45 Self {
46 interceptors_any: self.interceptors_any.clone(),
47 interceptors_key: self.interceptors_key.clone(),
48 subscribers_any: self.subscribers_any.clone(),
49 subscribers_key: self.subscribers_key.clone(),
50 next_id: self.next_id.clone(),
51 intercept_depth: self.intercept_depth.clone(),
52 cache: self.cache.clone(),
53 }
54 }
55}
56
57impl<K: std::fmt::Debug, V: std::fmt::Debug> std::fmt::Debug for ReactiveMapCore<K, V> {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 let mut d = f.debug_struct("ReactiveMapCore");
60 if let Ok(cache) = self.cache.try_lock() {
61 d.field("cache", &*cache);
62 } else {
63 d.field("cache", &"<locked>");
64 }
65 let interceptors_count = self
66 .interceptors_any
67 .try_lock()
68 .map(|l| l.len())
69 .unwrap_or(0);
70 let subscribers_count = self
71 .subscribers_any
72 .try_lock()
73 .map(|l| l.len())
74 .unwrap_or(0);
75 d.field("interceptors_any_count", &interceptors_count)
76 .field("subscribers_any_count", &subscribers_count)
77 .finish()
78 }
79}
80
81impl<K: ReactiveMapKey, V: ReactiveMapValue> Default for ReactiveMapCore<K, V> {
82 fn default() -> Self {
83 Self::new()
84 }
85}
86
87impl<K: ReactiveMapKey, V: ReactiveMapValue> ReactiveMapCore<K, V> {
88 pub fn new() -> Self {
89 Self {
90 interceptors_any: Arc::new(Mutex::new(Vec::new())),
91 interceptors_key: Arc::new(Mutex::new(HashMap::new())),
92 subscribers_any: Arc::new(Mutex::new(Vec::new())),
93 subscribers_key: Arc::new(Mutex::new(HashMap::new())),
94 next_id: Arc::new(AtomicU64::new(0)),
95 intercept_depth: Arc::new(AtomicUsize::new(0)),
96 cache: Arc::new(Mutex::new(HashMap::new())),
97 }
98 }
99
100 #[track_caller]
101 pub fn subscribe_any<F>(&self, callback: F) -> SignalSubscription
102 where
103 F: Fn(&MapChange<K, V>) + Send + Sync + 'static,
104 {
105 let location = std::panic::Location::caller();
106 let id = self.next_id.fetch_add(1, Ordering::Relaxed);
107 let meta = SubscriptionMeta {
108 id,
109 location,
110 name: None,
111 };
112 self.subscribers_any
113 .lock()
114 .unwrap()
115 .push((id, Arc::new(callback), meta));
116
117 let subs_for_name = self.subscribers_any.clone();
118 let set_name = Arc::new(move |name: &'static str| {
119 if let Ok(mut lock) = subs_for_name.lock()
120 && let Some(entry) = lock.iter_mut().find(|(i, _, _)| *i == id)
121 {
122 entry.2.name = Some(name);
123 }
124 });
125 let subs_for_cleanup = self.subscribers_any.clone();
126 SignalSubscription {
127 id,
128 location,
129 name: None,
130 set_name,
131 cleanup: Arc::new(move |id| {
132 if let Ok(mut lock) = subs_for_cleanup.lock() {
133 lock.retain(|(i, _, _)| *i != id);
134 }
135 }),
136 }
137 }
138
139 #[track_caller]
140 pub fn subscribe_key<F>(&self, key: K, callback: F) -> SignalSubscription
141 where
142 F: Fn(&MapChange<K, V>) + Send + Sync + 'static,
143 {
144 let location = std::panic::Location::caller();
145 let id = self.next_id.fetch_add(1, Ordering::Relaxed);
146 let meta = SubscriptionMeta {
147 id,
148 location,
149 name: None,
150 };
151 self.subscribers_key
152 .lock()
153 .unwrap()
154 .entry(key.clone())
155 .or_default()
156 .push((id, Arc::new(callback), meta));
157
158 let subs_for_name = self.subscribers_key.clone();
159 let key_for_name = key.clone();
160 let set_name = Arc::new(move |name: &'static str| {
161 if let Ok(mut lock) = subs_for_name.lock()
162 && let Some(list) = lock.get_mut(&key_for_name)
163 && let Some(entry) = list.iter_mut().find(|(i, _, _)| *i == id)
164 {
165 entry.2.name = Some(name);
166 }
167 });
168 let subs_for_cleanup = self.subscribers_key.clone();
169 SignalSubscription {
170 id,
171 location,
172 name: None,
173 set_name,
174 cleanup: Arc::new(move |id| {
175 if let Ok(mut lock) = subs_for_cleanup.lock()
176 && let Some(list) = lock.get_mut(&key)
177 {
178 list.retain(|(i, _, _)| *i != id);
179 }
180 }),
181 }
182 }
183
184 pub fn intercept<F>(&self, path: Arc<str>, callback: F) -> InterceptDisposer
185 where
186 F: Fn(MapChange<K, V>) -> Option<MapChange<K, V>> + Send + Sync + 'static,
187 {
188 let id = self.next_id.fetch_add(1, Ordering::Relaxed);
189 self.interceptors_any
190 .lock()
191 .unwrap()
192 .push((id, Arc::new(callback)));
193 let subs = self.interceptors_any.clone();
194 InterceptDisposer {
195 id,
196 path,
197 cleanup: Arc::new(move |id| {
198 if let Ok(mut lock) = subs.lock() {
199 lock.retain(|(i, _)| *i != id);
200 }
201 }),
202 }
203 }
204
205 pub fn intercept_key<F>(&self, key: K, callback: F) -> InterceptDisposer
206 where
207 F: Fn(MapChange<K, V>) -> Option<MapChange<K, V>> + Send + Sync + 'static,
208 {
209 let id = self.next_id.fetch_add(1, Ordering::Relaxed);
210 self.interceptors_key
211 .lock()
212 .unwrap()
213 .entry(key.clone())
214 .or_default()
215 .push((id, Arc::new(callback)));
216 let subs = self.interceptors_key.clone();
217 InterceptDisposer {
218 id,
219 path: Arc::from(""),
220 cleanup: Arc::new(move |id| {
221 if let Ok(mut lock) = subs.lock()
222 && let Some(list) = lock.get_mut(&key)
223 {
224 list.retain(|(i, _)| *i != id);
225 }
226 }),
227 }
228 }
229
230 pub fn run_interceptors(
231 &self,
232 path: Arc<str>,
233 mut change: MapChange<K, V>,
234 ) -> Result<MapChange<K, V>, String> {
235 if let Some(_guard) = InterceptGuard::enter(&self.intercept_depth, path) {
236 let mut keys_to_intercept = Vec::new();
237 if let Some(k) = change.key() {
238 keys_to_intercept.push(k.clone());
239 } else {
240 let lock = self.interceptors_key.lock().unwrap();
241 keys_to_intercept = lock.keys().cloned().collect();
242 }
243 for key in keys_to_intercept {
244 let interceptors = {
245 let lock = self.interceptors_key.lock().unwrap();
246 lock.get(&key).cloned().unwrap_or_default()
247 };
248 for (_, interceptor) in interceptors {
249 if let Some(new_change) = interceptor(change.clone()) {
250 change = new_change;
251 } else {
252 return Err("Map change intercepted by key filter".to_string());
253 }
254 }
255 }
256 let interceptors_any = self.interceptors_any.lock().unwrap().clone();
257 for (_, interceptor) in interceptors_any {
258 if let Some(new_change) = interceptor(change.clone()) {
259 change = new_change;
260 } else {
261 return Err("Map change intercepted by global filter".to_string());
262 }
263 }
264 }
265 Ok(change)
266 }
267
268 pub fn notify(&self, change: &MapChange<K, V>) {
269 if let Some(k) = change.key()
270 && let Ok(lock) = self.subscribers_key.lock()
271 && let Some(entries) = lock.get(k)
272 {
273 for (_, cb, meta) in entries {
274 tracing::trace!(
275 target: "amethystate",
276 subscription_id = meta.id,
277 name = meta.name,
278 location = format!("{}:{}", meta.location.file(), meta.location.line()),
279 "map signal emit → key subscription fire",
280 );
281 cb(change);
282 }
283 }
284
285 if let Ok(lock) = self.subscribers_any.lock() {
286 for (_, cb, meta) in lock.iter() {
287 tracing::trace!(
288 target: "amethystate",
289 subscription_id = meta.id,
290 name = meta.name,
291 location = format!("{}:{}", meta.location.file(), meta.location.line()),
292 "map signal emit → any subscription fire",
293 );
294 cb(change);
295 }
296 }
297 }
298}