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