cope/instance/
atom.rs

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    // Option dance
63    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                // https://github.com/rust-lang/rust/issues/51886
89                (&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}