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: Signal<T> = Signal::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()>>;
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            listeners = take(inner.get_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            inner.get_mut_listeners().extend(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()>>;
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            listeners = take(inner.get_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            inner.get_mut_listeners().extend(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()>>;
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            listeners = take(inner.get_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            inner.get_mut_listeners().extend(listeners);
185        }
186        schedule_signal_update();
187        true
188    }
189}
190
191/// Prevents direct dereference of a signal to enforce explicit API usage.
192impl<T> Deref for Signal<T>
193where
194    T: Clone + PartialEq + 'static,
195{
196    type Target = T;
197
198    fn deref(&self) -> &Self::Target {
199        panic!("Signal does not support direct dereference; use .get() instead");
200    }
201}
202
203/// Prevents direct mutable dereference of a signal to enforce explicit API usage.
204impl<T> DerefMut for Signal<T>
205where
206    T: Clone + PartialEq + 'static,
207{
208    fn deref_mut(&mut self) -> &mut Self::Target {
209        panic!("Signal does not support direct dereference; use .set() instead");
210    }
211}
212
213/// Clones the signal, sharing the same inner state.
214impl<T> Clone for Signal<T>
215where
216    T: Clone + PartialEq + 'static,
217{
218    fn clone(&self) -> Self {
219        *self
220    }
221}
222
223/// Copies the signal, sharing the same inner state.
224impl<T> Copy for Signal<T> where T: Clone + PartialEq + 'static {}
225
226/// SAFETY: `SignalCell` is only used in single-threaded WASM contexts.
227/// Concurrent access from multiple threads would be undefined behavior.
228unsafe impl<T> Sync for SignalCell<T> where T: Clone + PartialEq + 'static {}
229
230/// Implementation of SignalCell construction and access.
231impl<T> SignalCell<T>
232where
233    T: Clone + PartialEq + 'static,
234{
235    /// Creates a new empty `SignalCell` suitable for use in static contexts.
236    ///
237    /// # Returns
238    ///
239    /// - `Self` - An empty `SignalCell` with no signal stored.
240    pub const fn empty() -> Self {
241        Self {
242            inner: UnsafeCell::new(None),
243        }
244    }
245
246    /// Stores a signal into the cell.
247    ///
248    /// # Arguments
249    ///
250    /// - `Signal<T>` - The signal to store.
251    ///
252    /// # Panics
253    ///
254    /// Panics if a signal has already been stored.
255    pub fn set(&self, signal: Signal<T>) {
256        unsafe {
257            let ptr: &mut Option<Signal<T>> = &mut *self.get_inner().get();
258            if ptr.is_some() {
259                panic!("SignalCell::set called on an already-initialized cell");
260            }
261            *ptr = Some(signal);
262        }
263    }
264
265    /// Returns the signal stored in the cell.
266    ///
267    /// # Returns
268    ///
269    /// - `Signal<T>` - The stored signal.
270    ///
271    /// # Panics
272    ///
273    /// Panics if no signal has been stored via `set`.
274    pub fn get(&self) -> Signal<T> {
275        unsafe {
276            let ptr: &Option<Signal<T>> = &*self.get_inner().get();
277            match ptr {
278                Some(signal) => *signal,
279                None => panic!("SignalCell::get called on an uninitialized cell"),
280            }
281        }
282    }
283}
284
285/// Provides a default empty `SignalCell`.
286impl<T> Default for SignalCell<T>
287where
288    T: Clone + PartialEq + 'static,
289{
290    fn default() -> Self {
291        Self {
292            inner: UnsafeCell::new(None),
293        }
294    }
295}
296
297/// SAFETY: `SignalInnerRegistryCell` is only used in single-threaded WASM contexts.
298unsafe impl Sync for SignalInnerRegistryCell {}