amethystate_core/primitives/
field_core.rs1use crate::change::Change;
2use crate::primitives::intercept::{InterceptDisposer, InterceptGuard};
3use crate::primitives::signal::{Signal, SignalSubscription};
4use serde::Serialize;
5use serde::de::DeserializeOwned;
6use std::sync::atomic::{AtomicUsize, Ordering};
7use std::sync::{Arc, Mutex};
8use uuid::Uuid;
9
10pub trait FieldValue: DeserializeOwned + Serialize + Clone + Send + Sync + 'static {}
11impl<T: DeserializeOwned + Serialize + Clone + Send + Sync + 'static> FieldValue for T {}
12
13pub struct FieldCore<T> {
14 pub signal: Signal<T>,
15 pub interceptors: Arc<
16 Mutex<
17 Vec<(
18 u64,
19 Arc<dyn Fn(Change<T>) -> Option<Change<T>> + Send + Sync + 'static>,
20 )>,
21 >,
22 >,
23 pub next_interceptor_id: Arc<AtomicUsize>,
24 pub intercept_depth: Arc<AtomicUsize>,
25}
26
27impl<T> Clone for FieldCore<T> {
28 fn clone(&self) -> Self {
29 Self {
30 signal: self.signal.clone(),
31 interceptors: self.interceptors.clone(),
32 next_interceptor_id: self.next_interceptor_id.clone(),
33 intercept_depth: self.intercept_depth.clone(),
34 }
35 }
36}
37
38impl<T: Clone + 'static> FieldCore<T> {
39 pub fn new_with_signal(initial: Signal<T>) -> Self {
40 Self {
41 signal: initial,
42 interceptors: Arc::new(Mutex::new(Vec::new())),
43 next_interceptor_id: Arc::new(AtomicUsize::new(0)),
44 intercept_depth: Arc::new(AtomicUsize::new(0)),
45 }
46 }
47
48 pub fn new(initial: T) -> Self {
49 Self {
50 signal: Signal::new(initial),
51 interceptors: Arc::new(Mutex::new(Vec::new())),
52 next_interceptor_id: Arc::new(AtomicUsize::new(0)),
53 intercept_depth: Arc::new(AtomicUsize::new(0)),
54 }
55 }
56
57 pub fn get(&self) -> T {
58 self.signal.get()
59 }
60
61 #[track_caller]
62 pub fn subscribe<F>(&self, callback: F) -> SignalSubscription
63 where
64 F: Fn(T) + Send + Sync + 'static,
65 {
66 self.signal.subscribe(move |val: &T| {
67 callback(val.clone());
68 })
69 }
70
71 #[track_caller]
72 pub fn subscribe_with_source<F>(&self, callback: F) -> SignalSubscription
73 where
74 F: Fn(T, Option<Uuid>) + Send + Sync + 'static,
75 {
76 self.signal.subscribe_with_source(move |val: &T, src| {
77 callback(val.clone(), src);
78 })
79 }
80
81 pub fn intercept<F>(&self, path: Arc<str>, callback: F) -> InterceptDisposer
82 where
83 F: Fn(Change<T>) -> Option<Change<T>> + Send + Sync + 'static,
84 {
85 let id = self.next_interceptor_id.fetch_add(1, Ordering::Relaxed);
86 self.interceptors
87 .lock()
88 .unwrap()
89 .push((id as u64, Arc::new(callback)));
90
91 let interceptors = self.interceptors.clone();
92 InterceptDisposer {
93 id: id as u64,
94 path: path.clone(),
95 cleanup: Arc::new(move |id| {
96 if let Ok(mut lock) = interceptors.lock() {
97 lock.retain(|(i, _)| *i != id);
98 }
99 }),
100 }
101 }
102
103 pub fn run_interceptors(
104 &self,
105 path: Arc<str>,
106 value: T,
107 source: Option<Uuid>,
108 ) -> Result<Change<T>, String> {
109 let mut change = Change {
110 source,
111 old_value: self.get(),
112 new_value: value,
113 };
114
115 if let Some(_guard) = InterceptGuard::enter(&self.intercept_depth, path) {
116 let interceptors = { self.interceptors.lock().unwrap().clone() };
117 for (_, interceptor) in interceptors {
118 if let Some(new_change) = interceptor(change.clone()) {
119 change = new_change;
120 } else {
121 return Err("Change intercepted by core filter".to_string());
122 }
123 }
124 }
125 Ok(change)
126 }
127}