1use crate::instance::engine::Engine;
2use std::{
3 cell::{Ref, RefCell, RefMut},
4 ops::{Deref, DerefMut},
5 sync::Arc,
6};
7
8type SubscriptionList = Vec<Arc<RefCell<Vec<Subscription>>>>;
9type Subscription = Arc<RefCell<dyn FnMut()>>;
10
11pub struct Atom<T> {
12 engine: Arc<Engine>,
13 value: Arc<RefCell<T>>,
14 subscriptions: Arc<RefCell<SubscriptionList>>,
15}
16
17impl<T: 'static> Atom<T> {
18 pub fn new(engine: Arc<Engine>, initial_value: T) -> Self {
19 Self {
20 engine,
21 value: Arc::new(RefCell::new(initial_value)),
22 subscriptions: Arc::new(RefCell::new(Vec::new())),
23 }
24 }
25
26 #[must_use]
27 pub fn get(&self) -> Ref<'_, T> {
28 self.engine.track(&self.subscriptions);
29 self.value.borrow()
30 }
31
32 #[must_use]
33 pub fn get_mut(&self) -> AtomMut<'_, T> {
34 AtomMut {
35 value: Some(self.value.borrow_mut()),
36 subscriptions: self.subscriptions.clone(),
37 }
38 }
39
40 #[must_use]
41 pub fn sample_mut(&self) -> RefMut<'_, T> {
42 self.value.borrow_mut()
43 }
44
45 pub fn set(&self, value: T) {
46 *self.get_mut() = value;
47 }
48}
49
50impl<T> Clone for Atom<T> {
51 fn clone(&self) -> Self {
52 Self {
53 engine: self.engine.clone(),
54 value: self.value.clone(),
55 subscriptions: self.subscriptions.clone(),
56 }
57 }
58}
59
60#[allow(clippy::module_name_repetitions)]
61pub struct AtomMut<'a, T> {
62 value: Option<RefMut<'a, T>>,
64 subscriptions: Arc<RefCell<SubscriptionList>>,
65}
66
67impl<T> Deref for AtomMut<'_, T> {
68 type Target = T;
69
70 fn deref(&self) -> &Self::Target {
71 &*self.value.as_ref().unwrap()
72 }
73}
74
75impl<T> DerefMut for AtomMut<'_, T> {
76 fn deref_mut(&mut self) -> &mut Self::Target {
77 &mut *self.value.as_mut().unwrap()
78 }
79}
80
81impl<T> Drop for AtomMut<'_, T> {
82 fn drop(&mut self) {
83 drop(self.value.take());
84
85 for subscriptions in self.subscriptions.borrow().iter() {
86 for subscription in subscriptions.borrow_mut().iter_mut() {
87 let mut func = subscription.borrow_mut();
88 (&mut *func)();
90 }
91 }
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use crate::instance::{Atom, Engine};
98 use std::sync::Arc;
99
100 #[test]
101 fn get_initial_value() {
102 let engine = Arc::new(Engine::new());
103 let atom = Atom::new(engine, 123);
104 assert_eq!(*atom.get(), 123);
105 }
106
107 #[test]
108 fn set() {
109 let engine = Arc::new(Engine::new());
110 let atom = Atom::new(engine, 0);
111 atom.set(42);
112 assert_eq!(*atom.get(), 42);
113 }
114
115 #[test]
116 fn mutate() {
117 let engine = Arc::new(Engine::new());
118 let atom = Atom::new(engine, 10);
119 *atom.get_mut() += 1;
120 assert_eq!(*atom.get(), 11);
121 }
122}