hara_native/lang/data/
atom.rs1use std::fmt;
2use std::sync::{Arc, Mutex};
3
4use crate::lang::protocol::{IDeref, IDisplay, IReset, IWatch};
5
6type Validator<V> = Arc<dyn Fn(&V) -> bool + Send + Sync>;
7type Watch<V> = Arc<dyn Fn(&WatchEntry<V>) + Send + Sync>;
8
9#[derive(Clone)]
10pub struct WatchEntry<V> {
11 pub key: String,
12 pub old_value: V,
13 pub new_value: V,
14}
15
16#[derive(Clone)]
17pub struct Atom<V> {
18 state: Arc<Mutex<V>>,
19 validator: Option<Validator<V>>,
20 watches: Arc<Mutex<Vec<(String, Watch<V>)>>>,
21}
22impl<V> Atom<V> {
23 pub fn new(value: V) -> Self {
24 Self {
25 state: Arc::new(Mutex::new(value)),
26 validator: None,
27 watches: Arc::new(Mutex::new(Vec::new())),
28 }
29 }
30 pub fn same_identity(&self, other: &Self) -> bool {
31 Arc::ptr_eq(&self.state, &other.state)
32 }
33 pub fn identity_address(&self) -> usize {
34 Arc::as_ptr(&self.state) as usize
35 }
36 pub fn with_validator(
37 value: V,
38 validator: impl Fn(&V) -> bool + Send + Sync + 'static,
39 ) -> Self {
40 Self {
41 validator: Some(Arc::new(validator)),
42 ..Self::new(value)
43 }
44 }
45 pub fn add_watch(
46 &self,
47 key: impl Into<String>,
48 watch: impl Fn(&WatchEntry<V>) + Send + Sync + 'static,
49 ) {
50 let key = key.into();
51 let mut watches = self.watches.lock().expect("atom watches");
52 watches.retain(|(candidate, _)| candidate != &key);
53 watches.push((key, Arc::new(watch)));
54 }
55 pub fn remove_watch(&self, key: &str) {
56 self.watches
57 .lock()
58 .expect("atom watches")
59 .retain(|(candidate, _)| candidate != key);
60 }
61 fn accepts(&self, value: &V) -> bool {
62 self.validator
63 .as_ref()
64 .map_or(true, |validator| validator(value))
65 }
66}
67impl<V: Clone> Atom<V> {
68 pub fn deref_value(&self) -> V {
69 self.state.lock().expect("atom state").clone()
70 }
71 pub fn reset(&self, new_value: V) -> Result<V, String> {
72 if !self.accepts(&new_value) {
73 return Err("atom validator rejected value".into());
74 }
75 let old_value = {
76 let mut state = self.state.lock().expect("atom state");
77 std::mem::replace(&mut *state, new_value.clone())
78 };
79 self.notify(old_value, new_value.clone());
80 Ok(new_value)
81 }
82 pub fn swap(&self, f: impl FnOnce(&V) -> V) -> Result<V, String> {
83 let (old_value, new_value) = {
84 let mut state = self.state.lock().expect("atom state");
85 let old = state.clone();
86 let new = f(&old);
87 if !self.accepts(&new) {
88 return Err("atom validator rejected value".into());
89 }
90 *state = new.clone();
91 (old, new)
92 };
93 self.notify(old_value, new_value.clone());
94 Ok(new_value)
95 }
96 fn notify(&self, old_value: V, new_value: V) {
97 for (key, watch) in self.watches.lock().expect("atom watches").iter() {
98 watch(&WatchEntry {
99 key: key.clone(),
100 old_value: old_value.clone(),
101 new_value: new_value.clone(),
102 });
103 }
104 }
105}
106impl<V: Clone + PartialEq> Atom<V> {
107 pub fn compare_and_set(&self, old: &V, new: V) -> Result<bool, String> {
108 if !self.accepts(&new) {
109 return Ok(false);
110 }
111 let prior = {
112 let mut state = self.state.lock().expect("atom state");
113 if &*state != old {
114 return Ok(false);
115 }
116 let prior = state.clone();
117 *state = new.clone();
118 prior
119 };
120 self.notify(prior, new);
121 Ok(true)
122 }
123}
124impl<V: Clone> IDeref for Atom<V> {
125 type Output = V;
126 fn deref(&self) -> V {
127 self.deref_value()
128 }
129}
130impl<V: Clone> IReset<V> for Atom<V> {
131 type Error = String;
132
133 fn reset(&self, value: V) -> Result<V, Self::Error> {
134 Atom::reset(self, value)
135 }
136}
137impl<V: Clone> IWatch<V> for Atom<V> {
138 type Key = String;
139 type WatchEntry = WatchEntry<V>;
140
141 fn add_watch(&self, key: Self::Key, watch: impl Fn(&Self::WatchEntry) + Send + Sync + 'static) {
142 Atom::add_watch(self, key, watch);
143 }
144
145 fn remove_watch(&self, key: &Self::Key) {
146 Atom::remove_watch(self, key);
147 }
148
149 fn notify_watches(&self, old_value: V, new_value: V) {
150 self.notify(old_value, new_value);
151 }
152}
153impl<V: fmt::Display + Clone> IDisplay for Atom<V> {
154 fn display(&self) -> String {
155 format!("#atom <{}>", self.deref_value())
156 }
157}
158impl<V: fmt::Display + Clone> fmt::Display for Atom<V> {
159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160 f.write_str(&self.display())
161 }
162}
163impl<V> fmt::Debug for Atom<V> {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 f.debug_struct("Atom").finish_non_exhaustive()
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::Atom;
172 use crate::lang::protocol::{IReset, IWatch};
173 use std::sync::{Arc, Mutex};
174 #[test]
175 fn validates_swaps_and_notifies() {
176 let atom = Atom::with_validator(1, |v| *v >= 0);
177 let seen = Arc::new(Mutex::new(Vec::new()));
178 let output = seen.clone();
179 atom.add_watch("test", move |event| {
180 output
181 .lock()
182 .unwrap()
183 .push((event.old_value, event.new_value))
184 });
185 assert_eq!(atom.swap(|v| v + 2).unwrap(), 3);
186 assert!(atom.reset(-1).is_err());
187 assert_eq!(&*seen.lock().unwrap(), &[(1, 3)]);
188 assert_eq!(IReset::reset(&atom, 4).unwrap(), 4);
189 IWatch::remove_watch(&atom, &"test".to_string());
190 IWatch::notify_watches(&atom, 4, 5);
191 assert_eq!(seen.lock().unwrap().len(), 2);
192 }
193}