Skip to main content

euv_core/reactive/signal/
impl.rs

1use crate::*;
2
3/// Implementation of reactive signal operations.
4impl<T> Signal<T>
5where
6    T: Clone + PartialEq + 'static,
7{
8    /// Creates a new `Signal` with the given initial value.
9    ///
10    /// # Arguments
11    ///
12    /// - `T` - The initial value of the signal.
13    ///
14    /// # Returns
15    ///
16    /// - `Self` - A handle to the newly created reactive signal.
17    pub fn create(value: T) -> Self {
18        let signal_inner: Rc<RefCell<SignalInner<T>>> =
19            Rc::new(RefCell::new(SignalInner::new(value, Vec::new(), true)));
20        let addr: usize = Rc::as_ptr(&signal_inner) as usize;
21        signal_inner_registry_mut().insert(addr, signal_inner as Rc<dyn Any>);
22        let mut signal: Self = Self::new(0, std::marker::PhantomData);
23        signal.set_inner(addr);
24        signal
25    }
26
27    /// Returns the raw inner pointer address for identity comparison.
28    ///
29    /// # Returns
30    ///
31    /// - `usize` - The memory address of the inner `Rc`.
32    pub(crate) fn get_inner_addr(&self) -> usize {
33        self.get_inner()
34    }
35
36    /// Returns the current value of the signal.
37    ///
38    /// # Returns
39    ///
40    /// - `T` - The current value of the signal.
41    pub fn get(&self) -> T {
42        let rc: Rc<RefCell<SignalInner<T>>> = get_signal_inner_rc(self.get_inner());
43        rc.borrow().get_value().clone()
44    }
45
46    /// Attempts to return the current value of the signal without panicking.
47    ///
48    /// # Returns
49    ///
50    /// - `Some(T)` - The current value if the borrow succeeds.
51    /// - `None` - If the inner value is already mutably borrowed.
52    pub fn try_get(&self) -> Option<T> {
53        let rc: Rc<RefCell<SignalInner<T>>> = get_signal_inner_rc(self.get_inner());
54        Some(rc.borrow().get_value().clone())
55    }
56
57    /// Subscribes a callback to be invoked when the signal changes.
58    ///
59    /// # Arguments
60    ///
61    /// - `FnMut() + 'static` - The callback to invoke when the signal changes.
62    pub fn subscribe<F>(&self, callback: F)
63    where
64        F: FnMut() + 'static,
65    {
66        let rc: Rc<RefCell<SignalInner<T>>> = get_signal_inner_rc(self.get_inner());
67        rc.borrow_mut().get_mut_listeners().push(Box::new(callback));
68    }
69
70    /// Replaces all listeners with a single new callback.
71    ///
72    /// Unlike `subscribe`, which appends a listener, this method clears any
73    /// existing listeners first and then adds the new one.
74    ///
75    /// # Arguments
76    ///
77    /// - `FnMut() + 'static` - The callback to invoke when the signal changes.
78    pub fn replace_subscribe<F>(&self, callback: F)
79    where
80        F: FnMut() + 'static,
81    {
82        let rc: Rc<RefCell<SignalInner<T>>> = get_signal_inner_rc(self.get_inner());
83        let mut inner: RefMut<SignalInner<T>> = rc.borrow_mut();
84        let listeners: &mut Vec<Box<dyn FnMut()>> = inner.get_mut_listeners();
85        listeners.clear();
86        listeners.push(Box::new(callback));
87    }
88
89    /// Removes all subscribed listeners from this signal and marks it as
90    /// inactive.
91    pub fn clear_listeners(&self) {
92        let rc: Rc<RefCell<SignalInner<T>>> = get_signal_inner_rc(self.get_inner());
93        let mut inner: RefMut<SignalInner<T>> = rc.borrow_mut();
94        inner.set_alive(false);
95        inner.get_mut_listeners().clear();
96    }
97
98    /// Sets the value of the signal and notifies listeners.
99    ///
100    /// # Arguments
101    ///
102    /// - `T` - The new value to assign to the signal.
103    pub fn set(&self, value: T) {
104        let rc: Rc<RefCell<SignalInner<T>>> = get_signal_inner_rc(self.get_inner());
105        let mut listeners: Vec<Box<dyn FnMut()>> = Vec::new();
106        {
107            let mut inner: RefMut<SignalInner<T>> = rc.borrow_mut();
108            if !inner.get_alive() {
109                return;
110            }
111            if *inner.get_value() == value {
112                return;
113            }
114            inner.set_value(value);
115            swap(inner.get_mut_listeners(), &mut listeners);
116        }
117        for listener in listeners.iter_mut() {
118            listener();
119        }
120        {
121            let mut inner: RefMut<SignalInner<T>> = rc.borrow_mut();
122            swap(inner.get_mut_listeners(), &mut listeners);
123        }
124        schedule_signal_update();
125    }
126
127    /// Sets the value of the signal and notifies listeners without scheduling
128    /// a global DOM update dispatch.
129    ///
130    /// # Arguments
131    ///
132    /// - `T` - The new value to assign to the signal.
133    pub fn set_silent(&self, value: T) {
134        let rc: Rc<RefCell<SignalInner<T>>> = get_signal_inner_rc(self.get_inner());
135        let mut listeners: Vec<Box<dyn FnMut()>> = Vec::new();
136        {
137            let mut inner: RefMut<SignalInner<T>> = rc.borrow_mut();
138            if !inner.get_alive() {
139                return;
140            }
141            if *inner.get_value() == value {
142                return;
143            }
144            inner.set_value(value);
145            swap(inner.get_mut_listeners(), &mut listeners);
146        }
147        for listener in listeners.iter_mut() {
148            listener();
149        }
150        {
151            let mut inner: RefMut<SignalInner<T>> = rc.borrow_mut();
152            swap(inner.get_mut_listeners(), &mut listeners);
153        }
154    }
155
156    /// Attempts to set the value of the signal and notify listeners without panicking.
157    ///
158    /// # Arguments
159    ///
160    /// - `T` - The new value to assign to the signal.
161    ///
162    /// # Returns
163    ///
164    /// - `bool` - `true` if the value was successfully updated and listeners were notified, `false` if unchanged or inactive.
165    pub fn try_set(&self, value: T) -> bool {
166        let rc: Rc<RefCell<SignalInner<T>>> = get_signal_inner_rc(self.get_inner());
167        let mut listeners: Vec<Box<dyn FnMut()>> = Vec::new();
168        {
169            let mut inner: RefMut<SignalInner<T>> = rc.borrow_mut();
170            if !inner.get_alive() {
171                return false;
172            }
173            if *inner.get_value() == value {
174                return false;
175            }
176            inner.set_value(value);
177            swap(inner.get_mut_listeners(), &mut listeners);
178        }
179        for listener in listeners.iter_mut() {
180            listener();
181        }
182        {
183            let mut inner: RefMut<SignalInner<T>> = rc.borrow_mut();
184            swap(inner.get_mut_listeners(), &mut listeners);
185        }
186        schedule_signal_update();
187        true
188    }
189}
190
191/// Prevents direct dereference of a signal to enforce explicit API usage.
192///
193/// Panics at runtime to guide users towards `.get()` instead.
194impl<T> Deref for Signal<T>
195where
196    T: Clone + PartialEq + 'static,
197{
198    type Target = T;
199
200    fn deref(&self) -> &Self::Target {
201        panic!("Signal does not support direct dereference; use .get() instead");
202    }
203}
204
205/// Prevents direct mutable dereference of a signal to enforce explicit API usage.
206///
207/// Panics at runtime to guide users towards `.set()` instead.
208impl<T> DerefMut for Signal<T>
209where
210    T: Clone + PartialEq + 'static,
211{
212    fn deref_mut(&mut self) -> &mut Self::Target {
213        panic!("Signal does not support direct dereference; use .set() instead");
214    }
215}
216
217/// Clones the signal, sharing the same inner state.
218///
219/// Since `Signal` is `Copy`, this simply returns `*self`.
220///
221/// # Returns
222///
223/// - `Self`: A copy of the signal handle sharing the same inner state.
224impl<T> Clone for Signal<T>
225where
226    T: Clone + PartialEq + 'static,
227{
228    fn clone(&self) -> Self {
229        *self
230    }
231}
232
233/// Copies the signal, sharing the same inner state.
234///
235/// Safe because only the inner address (a `usize`) is copied;
236/// the actual `Rc` reference is held by the global signal registry.
237impl<T> Copy for Signal<T> where T: Clone + PartialEq + 'static {}
238
239/// Marks `SignalCell` as `Sync` for single-threaded WASM contexts.
240///
241/// SAFETY: `SignalCell` is only used in single-threaded WASM contexts.
242/// Concurrent access from multiple threads would be undefined behavior.
243unsafe impl<T> Sync for SignalCell<T> where T: Clone + PartialEq + 'static {}
244
245/// Implementation of SignalCell construction and access.
246impl<T> SignalCell<T>
247where
248    T: Clone + PartialEq + 'static,
249{
250    /// Creates a new empty `SignalCell` suitable for use in static contexts.
251    ///
252    /// # Returns
253    ///
254    /// - `Self` - An empty `SignalCell` with no signal stored.
255    pub const fn empty() -> Self {
256        Self {
257            inner: UnsafeCell::new(None),
258        }
259    }
260
261    /// Stores a signal into the cell.
262    ///
263    /// # Arguments
264    ///
265    /// - `Signal<T>` - The signal to store.
266    ///
267    /// # Panics
268    ///
269    /// Panics if a signal has already been stored.
270    pub fn set(&self, signal: Signal<T>) {
271        unsafe {
272            let ptr: &mut Option<Signal<T>> = &mut *self.get_inner().get();
273            if ptr.is_some() {
274                panic!("SignalCell::set called on an already-initialized cell");
275            }
276            *ptr = Some(signal);
277        }
278    }
279
280    /// Returns the signal stored in the cell.
281    ///
282    /// # Returns
283    ///
284    /// - `Signal<T>` - The stored signal.
285    ///
286    /// # Panics
287    ///
288    /// Panics if no signal has been stored via `set`.
289    pub fn get(&self) -> Signal<T> {
290        unsafe {
291            let ptr: &Option<Signal<T>> = &*self.get_inner().get();
292            match ptr {
293                Some(signal) => *signal,
294                None => panic!("SignalCell::get called on an uninitialized cell"),
295            }
296        }
297    }
298}
299
300/// Provides a default empty `SignalCell`.
301///
302/// Creates a `SignalCell` with `None` stored in the inner `UnsafeCell`.
303///
304/// # Returns
305///
306/// - `Self`: An empty `SignalCell` with no signal stored.
307impl<T> Default for SignalCell<T>
308where
309    T: Clone + PartialEq + 'static,
310{
311    fn default() -> Self {
312        Self {
313            inner: UnsafeCell::new(None),
314        }
315    }
316}
317
318/// Marks `SignalInnerRegistryCell` as `Sync` for single-threaded WASM contexts.
319///
320/// SAFETY: `SignalInnerRegistryCell` is only used in single-threaded WASM contexts.
321/// Concurrent access from multiple threads would be undefined behavior.
322unsafe impl Sync for SignalInnerRegistryCell {}