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