amethystate_core/primitives/
field_core.rs1use crate::change::Change;
2use crate::path::StorePath;
3use crate::primitives::intercept::{InterceptDepth, InterceptDisposer, InterceptGuard};
4use crate::primitives::signal::{Signal, SignalSubscription, held};
5use serde::Serialize;
6use serde::de::DeserializeOwned;
7use std::sync::atomic::{AtomicUsize, Ordering};
8use std::sync::{Arc, Mutex};
9use uuid::Uuid;
10
11pub trait FieldValue: DeserializeOwned + Serialize + Clone + Send + Sync + 'static {}
12impl<T: DeserializeOwned + Serialize + Clone + Send + Sync + 'static> FieldValue for T {}
13
14pub type DeclaredRule<T> = Arc<dyn Fn(&mut T) -> Result<(), String> + Send + Sync + 'static>;
17
18pub struct FieldCore<T> {
19 pub signal: Signal<T>,
20 pub interceptors: Arc<
21 Mutex<
22 Vec<(
23 u64,
24 Arc<dyn Fn(Change<T>) -> Option<Change<T>> + Send + Sync + 'static>,
25 )>,
26 >,
27 >,
28 pub next_interceptor_id: Arc<AtomicUsize>,
29 pub intercept_depth: InterceptDepth,
30 pub rules: Arc<Mutex<Vec<DeclaredRule<T>>>>,
31}
32
33impl<T> Clone for FieldCore<T> {
34 fn clone(&self) -> Self {
35 Self {
36 signal: self.signal.clone(),
37 interceptors: self.interceptors.clone(),
38 next_interceptor_id: self.next_interceptor_id.clone(),
39 intercept_depth: self.intercept_depth.clone(),
40 rules: self.rules.clone(),
41 }
42 }
43}
44
45impl<T: Clone + 'static> FieldCore<T> {
46 pub fn new_with_signal(initial: Signal<T>) -> Self {
47 Self {
48 signal: initial,
49 interceptors: Arc::new(Mutex::new(Vec::new())),
50 next_interceptor_id: Arc::new(AtomicUsize::new(0)),
51 intercept_depth: InterceptDepth::default(),
52 rules: Arc::new(Mutex::new(Vec::new())),
53 }
54 }
55
56 pub fn new(initial: T) -> Self {
57 Self {
58 signal: Signal::new(initial),
59 interceptors: Arc::new(Mutex::new(Vec::new())),
60 next_interceptor_id: Arc::new(AtomicUsize::new(0)),
61 intercept_depth: InterceptDepth::default(),
62 rules: Arc::new(Mutex::new(Vec::new())),
63 }
64 }
65
66 pub fn rule<F>(&self, rule: F)
69 where
70 F: Fn(&mut T) -> Result<(), String> + Send + Sync + 'static,
71 {
72 held(&self.rules).push(Arc::new(rule));
73 }
74
75 pub fn ruled(&self, value: &mut T) -> Result<(), String> {
77 let rules = held(&self.rules).clone();
78 for rule in rules {
79 rule(value)?;
80 }
81 Ok(())
82 }
83
84 pub fn get(&self) -> T {
85 self.signal.get()
86 }
87
88 #[track_caller]
89 pub fn subscribe<F>(&self, callback: F) -> SignalSubscription
90 where
91 F: for<'a> Fn(&'a T) + Send + Sync + 'static,
92 {
93 self.signal.subscribe(callback)
94 }
95
96 #[track_caller]
97 pub fn subscribe_with_source<F>(&self, callback: F) -> SignalSubscription
98 where
99 F: for<'a> Fn(&'a T, Option<Uuid>) + Send + Sync + 'static,
100 {
101 self.signal.subscribe_with_source(callback)
102 }
103
104 pub fn intercept<F>(&self, path: StorePath, callback: F) -> InterceptDisposer
105 where
106 F: Fn(Change<T>) -> Option<Change<T>> + Send + Sync + 'static,
107 {
108 let id = self.next_interceptor_id.fetch_add(1, Ordering::Relaxed);
109 held(&self.interceptors).push((id as u64, Arc::new(callback)));
110
111 let interceptors = self.interceptors.clone();
112 InterceptDisposer {
113 id: id as u64,
114 path: path.clone(),
115 cleanup: Arc::new(move |id| {
116 held(&interceptors).retain(|(i, _)| *i != id);
117 }),
118 }
119 }
120
121 pub fn run_interceptors(
122 &self,
123 path: StorePath,
124 value: T,
125 source: Option<Uuid>,
126 ) -> Result<Change<T>, crate::primitives::intercept::Refusal> {
127 use crate::primitives::intercept::Refusal;
128
129 let mut change = Change {
130 source,
131 old_value: self.get(),
132 new_value: value,
133 };
134
135 let Some(_guard) = InterceptGuard::enter(&self.intercept_depth, path) else {
136 return Err(Refusal::Recursed);
137 };
138
139 let interceptors = held(&self.interceptors).clone();
140 for (_, interceptor) in interceptors {
141 if let Some(new_change) = interceptor(change.clone()) {
142 change = new_change;
143 } else {
144 return Err(Refusal::Said(
145 "refused by an interceptor on the field".to_string(),
146 ));
147 }
148 }
149
150 self.ruled(&mut change.new_value).map_err(Refusal::Ruled)?;
151
152 Ok(change)
153 }
154}