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 {}