repose_core/
signal.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3use std::sync::atomic::{AtomicUsize, Ordering};
4
5use crate::reactive;
6
7pub type SubId = usize;
8
9static NEXT_SIGNAL_ID: AtomicUsize = AtomicUsize::new(1);
10
11pub struct Signal<T: 'static>(Rc<RefCell<Inner<T>>>);
12
13impl<T> Clone for Signal<T> {
14    fn clone(&self) -> Self {
15        Self(self.0.clone())
16    }
17}
18
19struct Inner<T> {
20    id: usize,
21    value: T,
22    subs: Vec<Option<Box<dyn Fn(&T)>>>,
23}
24
25impl<T> Signal<T> {
26    pub fn new(value: T) -> Self {
27        let id = NEXT_SIGNAL_ID.fetch_add(1, Ordering::Relaxed);
28        Self(Rc::new(RefCell::new(Inner {
29            id,
30            value,
31            subs: Vec::new(),
32        })))
33    }
34    pub fn id(&self) -> usize {
35        self.0.borrow().id
36    }
37    pub fn get(&self) -> T
38    where
39        T: Clone,
40    {
41        let inner = self.0.borrow();
42        reactive::register_signal_read(inner.id);
43        inner.value.clone()
44    }
45    pub fn set(&self, v: T) {
46        let mut inner = self.0.borrow_mut();
47        inner.value = v;
48        let vref = &inner.value;
49        for s in &inner.subs {
50            if let Some(cb) = s.as_ref() {
51                cb(vref);
52            }
53        }
54        // notify reactive graph
55        reactive::signal_changed(inner.id);
56    }
57    pub fn update<F: FnOnce(&mut T)>(&self, f: F) {
58        let mut inner = self.0.borrow_mut();
59        f(&mut inner.value);
60        let vref = &inner.value;
61        for s in &inner.subs {
62            if let Some(cb) = s.as_ref() {
63                cb(vref);
64            }
65        }
66        reactive::signal_changed(inner.id);
67    }
68    pub fn subscribe(&self, f: impl Fn(&T) + 'static) -> SubId {
69        self.0.borrow_mut().subs.push(Some(Box::new(f)));
70        self.0.borrow().subs.len() - 1
71    }
72    /// Remove a subscriber by id. Returns true if removed.
73    pub fn unsubscribe(&self, id: SubId) -> bool {
74        let mut inner = self.0.borrow_mut();
75        if id < inner.subs.len() {
76            inner.subs[id] = None;
77            true
78        } else {
79            false
80        }
81    }
82    /// Subscribe and get a guard that auto-unsubscribes on drop.
83    pub fn subscribe_guard(&self, f: impl Fn(&T) + 'static) -> SubGuard<T> {
84        let id = self.subscribe(f);
85        let sig = self.clone();
86        SubGuard {
87            sig,
88            id,
89            active: true,
90        }
91    }
92}
93
94pub fn signal<T>(t: T) -> Signal<T> {
95    Signal::new(t)
96}
97
98/// RAII guard for a Signal subscription; unsubscribes on drop.
99pub struct SubGuard<T: 'static> {
100    sig: crate::Signal<T>,
101    id: SubId,
102    active: bool,
103}
104impl<T> Drop for SubGuard<T> {
105    fn drop(&mut self) {
106        if self.active {
107            let _ = self.sig.unsubscribe(self.id);
108            self.active = false;
109        }
110    }
111}