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