sycamore_reactive/signals.rs
1//! Reactive signals.
2
3use std::cell::{Ref, RefMut};
4use std::fmt;
5use std::fmt::Formatter;
6use std::hash::Hash;
7use std::marker::PhantomData;
8use std::ops::{AddAssign, Deref, DivAssign, MulAssign, RemAssign, SubAssign};
9
10use slotmap::Key;
11use smallvec::SmallVec;
12
13use crate::*;
14
15/// A read-only reactive value.
16///
17/// Unlike the difference between Rust's shared and mutable-references (`&T` and `&mut`), the
18/// underlying data is not immutable. The data can be updated with the corresponding [`Signal`]
19/// (which has mutable access) and will show up in the `ReadSignal` as well.
20///
21/// A `ReadSignal` can be simply obtained by dereferencing a [`Signal`]. In fact, every [`Signal`]
22/// is a `ReadSignal` with additional write abilities!
23///
24/// # Example
25/// ```
26/// # use sycamore_reactive::*;
27/// # create_root(|| {
28/// let signal: Signal<i32> = create_signal(123);
29/// let read_signal: ReadSignal<i32> = *signal;
30/// assert_eq!(read_signal.get(), 123);
31/// signal.set(456);
32/// assert_eq!(read_signal.get(), 456);
33/// // read_signal.set(789); // <-- This is not allowed!
34/// # });
35/// ```
36///
37/// See [`create_signal`] for more information.
38pub struct ReadSignal<T: 'static> {
39 pub(crate) id: NodeId,
40 root: &'static Root,
41 /// Keep track of where the signal was created for diagnostics.
42 /// This is also stored in the Node but we want to have access to this when accessing a
43 /// disposed node so we store it here as well.
44 #[cfg(debug_assertions)]
45 created_at: &'static std::panic::Location<'static>,
46 _phantom: PhantomData<T>,
47}
48
49/// A reactive value that can be read and written to.
50///
51/// This is the writable version of [`ReadSignal`].
52///
53/// See [`create_signal`] for more information.
54pub struct Signal<T: 'static>(pub(crate) ReadSignal<T>);
55
56/// Create a new [`Signal`].
57///
58/// Signals are reactive atoms, pieces of state that can be read and written to and which will
59/// automatically update anything which depend on them.
60///
61/// # Usage
62/// The simplest way to use a signal is by using [`.get()`](ReadSignal::get) and
63/// [`.set(...)`](Signal::set). However, this only works if the value implements [`Copy`]. If
64/// we wanted to store something that doesn't implement [`Copy`] but implements [`Clone`] instead,
65/// say a [`String`], we can use [`.get_clone()`](ReadSignal::get_clone) which will automatically
66/// clone the value for us.
67///
68/// ```rust
69/// # use sycamore_reactive::*;
70/// # create_root(|| {
71/// let signal = create_signal(1);
72/// signal.get(); // Should return 1.
73/// signal.set(2);
74/// signal.get(); // Should return 2.
75/// # });
76/// ```
77///
78/// There are many other ways of getting and setting signals, such as
79/// [`.with(...)`](ReadSignal::with) and [`.update(...)`](Signal::update) which can access the
80/// signal even if it does not implement [`Clone`] or if you simply don't want to pay the
81/// performance overhead of cloning your value every time you read it.
82///
83/// # Reactivity
84/// What makes signals so powerful, as opposed to some other wrapper type like
85/// [`RefCell`](std::cell::RefCell) is the automatic dependency tracking. This means that accessing
86/// a signal will automatically add it as a dependency in certain contexts (such as inside a
87/// [`create_memo`](crate::create_memo)) which allows us to update related state whenever the signal
88/// is changed.
89///
90/// ```rust
91/// # use sycamore_reactive::*;
92/// # create_root(|| {
93/// let signal = create_signal(1);
94/// // Note that we are accessing signal inside a closure in the line below. This will cause it to
95/// // be automatically tracked and update our double value whenever signal is changed.
96/// let double = create_memo(move || signal.get() * 2);
97/// double.get(); // Should return 2.
98/// signal.set(2);
99/// double.get(); // Should return 4. Notice how this value was updated automatically when we
100/// // modified signal. This way, we can rest assured that all our state will be
101/// // consistent at all times!
102/// # });
103/// ```
104///
105/// # Ownership
106/// Signals are always associated with a reactive node. This is what performs the memory management
107/// for the actual value of the signal. What is returned from this function is just a
108/// handle/reference to the signal allocated in the reactive node. This allows us to freely copy
109/// this handle around and use it in closures and event handlers without worrying about ownership of
110/// the signal.
111///
112/// This is why in the above example, we could access `signal` even after it was moved in to the
113/// closure of the `create_memo`.
114#[cfg_attr(debug_assertions, track_caller)]
115pub fn create_signal<T>(value: T) -> Signal<T> {
116 let signal = create_empty_signal();
117 signal.get_mut().value = Some(Box::new(value));
118 signal
119}
120
121/// Creates a new [`Signal`] with the `value` field set to `None`.
122#[cfg_attr(debug_assertions, track_caller)]
123pub(crate) fn create_empty_signal<T>() -> Signal<T> {
124 let root = Root::global();
125 let id = root.nodes.borrow_mut().insert(ReactiveNode {
126 value: None,
127 callback: None,
128 children: Vec::new(),
129 parent: root.current_node.get(),
130 dependents: Vec::new(),
131 dependencies: SmallVec::new(),
132 cleanups: Vec::new(),
133 context: Vec::new(),
134 state: NodeState::Clean,
135 mark: Mark::None,
136 #[cfg(debug_assertions)]
137 created_at: std::panic::Location::caller(),
138 });
139 // Add the signal to the parent's `children` list.
140 let current_node = root.current_node.get();
141 if !current_node.is_null() {
142 root.nodes.borrow_mut()[current_node].children.push(id);
143 }
144
145 Signal(ReadSignal {
146 id,
147 root,
148 #[cfg(debug_assertions)]
149 created_at: std::panic::Location::caller(),
150 _phantom: PhantomData,
151 })
152}
153
154impl<T> ReadSignal<T> {
155 /// Get a immutable reference to the underlying node.
156 #[cfg_attr(debug_assertions, track_caller)]
157 pub(crate) fn get_ref(self) -> Ref<'static, ReactiveNode> {
158 Ref::map(
159 self.root
160 .nodes
161 .try_borrow()
162 .expect("cannot read signal while updating"),
163 |nodes| match nodes.get(self.id) {
164 Some(node) => node,
165 None => panic!("{}", self.get_disposed_panic_message()),
166 },
167 )
168 }
169
170 /// Get a mutable reference to the underlying node.
171 #[cfg_attr(debug_assertions, track_caller)]
172 pub(crate) fn get_mut(self) -> RefMut<'static, ReactiveNode> {
173 RefMut::map(
174 self.root
175 .nodes
176 .try_borrow_mut()
177 .expect("cannot update signal while reading"),
178 |nodes| match nodes.get_mut(self.id) {
179 Some(node) => node,
180 None => panic!("{}", self.get_disposed_panic_message()),
181 },
182 )
183 }
184
185 /// Returns `true` if the signal is still alive, i.e. has not yet been disposed.
186 pub fn is_alive(self) -> bool {
187 self.root.nodes.borrow().get(self.id).is_some()
188 }
189
190 /// Disposes the signal, i.e. frees up the memory held on by this signal. Accessing a signal
191 /// after it has been disposed immediately causes a panic.
192 pub fn dispose(self) {
193 NodeHandle(self.id, self.root).dispose();
194 }
195
196 fn get_disposed_panic_message(self) -> String {
197 #[cfg(not(debug_assertions))]
198 return "signal was disposed".to_string();
199
200 #[cfg(debug_assertions)]
201 return format!("signal was disposed. Created at {}", self.created_at);
202 }
203
204 /// Get the value of the signal without tracking it. The type must implement [`Copy`]. If this
205 /// is not the case, use [`ReadSignal::get_clone_untracked`] or [`ReadSignal::with_untracked`]
206 /// instead.
207 ///
208 /// # Example
209 /// ```
210 /// # use sycamore_reactive::*;
211 /// # create_root(|| {
212 /// let state = create_signal(0);
213 /// // Note that we have used `get_untracked` here so the signal is not actually being tracked
214 /// // by the memo.
215 /// let doubled = create_memo(move || state.get_untracked() * 2);
216 /// state.set(1);
217 /// assert_eq!(doubled.get(), 0);
218 /// # });
219 /// ```
220 #[cfg_attr(debug_assertions, track_caller)]
221 pub fn get_untracked(self) -> T
222 where
223 T: Copy,
224 {
225 self.with_untracked(|value| *value)
226 }
227
228 /// Get the value of the signal without tracking it. The type is [`Clone`]-ed automatically.
229 ///
230 /// This is the cloned equivalent of [`ReadSignal::get_untracked`].
231 #[cfg_attr(debug_assertions, track_caller)]
232 pub fn get_clone_untracked(self) -> T
233 where
234 T: Clone,
235 {
236 self.with_untracked(Clone::clone)
237 }
238
239 /// Get the value of the signal. The type must implement [`Copy`]. If this is not the case, use
240 /// [`ReadSignal::get_clone_untracked`] or [`ReadSignal::with_untracked`] instead.
241 ///
242 /// When called inside a reactive scope, the signal will be automatically tracked.
243 ///
244 /// # Example
245 /// ```
246 /// # use sycamore_reactive::*;
247 /// # create_root(|| {
248 /// let state = create_signal(0);
249 /// assert_eq!(state.get(), 0);
250 ///
251 /// state.set(1);
252 /// assert_eq!(state.get(), 1);
253 ///
254 /// // The signal is automatically tracked in the line below.
255 /// let doubled = create_memo(move || state.get());
256 /// # });
257 /// ```
258 #[cfg_attr(debug_assertions, track_caller)]
259 pub fn get(self) -> T
260 where
261 T: Copy,
262 {
263 self.track();
264 self.get_untracked()
265 }
266
267 /// Get the value of the signal. The type is [`Clone`]-ed automatically.
268 ///
269 /// When called inside a reactive scope, the signal will be automatically tracked.
270 ///
271 /// If the value implements [`Copy`], you should use [`ReadSignal::get`] instead.
272 ///
273 /// # Example
274 /// ```
275 /// # use sycamore_reactive::*;
276 /// # create_root(|| {
277 /// let greeting = create_signal("Hello".to_string());
278 /// assert_eq!(greeting.get_clone(), "Hello".to_string());
279 ///
280 /// // The signal is automatically tracked in the line below.
281 /// let hello_world = create_memo(move || format!("{} World!", greeting.get_clone()));
282 /// assert_eq!(hello_world.get_clone(), "Hello World!");
283 ///
284 /// greeting.set("Goodbye".to_string());
285 /// assert_eq!(greeting.get_clone(), "Goodbye".to_string());
286 /// assert_eq!(hello_world.get_clone(), "Goodbye World!");
287 /// # });
288 /// ```
289 #[cfg_attr(debug_assertions, track_caller)]
290 pub fn get_clone(self) -> T
291 where
292 T: Clone,
293 {
294 self.track();
295 self.get_clone_untracked()
296 }
297
298 /// Get a value from the signal without tracking it.
299 #[cfg_attr(debug_assertions, track_caller)]
300 pub fn with_untracked<U>(self, f: impl FnOnce(&T) -> U) -> U {
301 let node = self.get_ref();
302 let value = node
303 .value
304 .as_ref()
305 .expect("cannot read signal while updating");
306 let ret = f(value.downcast_ref().expect("wrong signal type"));
307 ret
308 }
309
310 /// Get a value from the signal.
311 ///
312 /// When called inside a reactive scope, the signal will be automatically tracked.
313 #[cfg_attr(debug_assertions, track_caller)]
314 pub fn with<U>(self, f: impl FnOnce(&T) -> U) -> U {
315 self.track();
316 self.with_untracked(f)
317 }
318
319 /// Creates a new [memo](create_memo) from this signal and a function. The resulting memo will
320 /// be created in the current reactive scope.
321 ///
322 /// # Example
323 /// ```
324 /// # use sycamore_reactive::*;
325 /// # create_root(|| {
326 /// let state = create_signal(0);
327 /// let doubled = state.map(|val| *val * 2);
328 /// assert_eq!(doubled.get(), 0);
329 /// state.set(1);
330 /// assert_eq!(doubled.get(), 2);
331 /// # });
332 /// ```
333 #[cfg_attr(debug_assertions, track_caller)]
334 pub fn map<U>(self, mut f: impl FnMut(&T) -> U + 'static) -> ReadSignal<U> {
335 create_memo(move || self.with(&mut f))
336 }
337
338 /// Track the signal in the current reactive scope. This is done automatically when calling
339 /// [`ReadSignal::get`] and other similar methods.
340 ///
341 /// # Example
342 /// ```
343 /// # use sycamore_reactive::*;
344 /// # create_root(|| {
345 /// let state = create_signal(0);
346 /// create_effect(move || {
347 /// state.track(); // Track the signal without getting its value.
348 /// println!("Yipee!");
349 /// });
350 /// state.set(1); // Prints "Yipee!"
351 /// # });
352 /// ```
353 pub fn track(self) {
354 if let Some(tracker) = &mut *self.root.tracker.borrow_mut() {
355 tracker.dependencies.push(self.id);
356 }
357 }
358}
359
360impl<T> Signal<T> {
361 /// Silently set a new value for the signal. This will not trigger any updates in dependent
362 /// signals. As such, this is generally not recommended as it can easily lead to state
363 /// inconsistencies.
364 ///
365 /// # Example
366 /// ```
367 /// # use sycamore_reactive::*;
368 /// # create_root(|| {
369 /// let state = create_signal(0);
370 /// let doubled = create_memo(move || state.get() * 2);
371 /// assert_eq!(doubled.get(), 0);
372 /// state.set_silent(1);
373 /// assert_eq!(doubled.get(), 0); // We now have inconsistent state!
374 /// # });
375 /// ```
376 #[cfg_attr(debug_assertions, track_caller)]
377 pub fn set_silent(self, new: T) {
378 self.replace_silent(new);
379 }
380
381 /// Set a new value for the signal and automatically update any dependents.
382 ///
383 /// # Example
384 /// ```
385 /// # use sycamore_reactive::*;
386 /// # create_root(|| {
387 /// let state = create_signal(0);
388 /// let doubled = create_memo(move || state.get() * 2);
389 /// assert_eq!(doubled.get(), 0);
390 /// state.set(1);
391 /// assert_eq!(doubled.get(), 2);
392 /// # });
393 /// ```
394 #[cfg_attr(debug_assertions, track_caller)]
395 pub fn set(self, new: T) {
396 self.replace(new);
397 }
398
399 /// Silently set a new value for the signal and return the previous value.
400 ///
401 /// This is the silent version of [`Signal::replace`].
402 #[cfg_attr(debug_assertions, track_caller)]
403 pub fn replace_silent(self, new: T) -> T {
404 self.update_silent(|val| std::mem::replace(val, new))
405 }
406
407 /// Set a new value for the signal and return the previous value.
408 ///
409 /// # Example
410 /// ```
411 /// # use sycamore_reactive::*;
412 /// # create_root(|| {
413 /// let state = create_signal(123);
414 /// let prev = state.replace(456);
415 /// assert_eq!(state.get(), 456);
416 /// assert_eq!(prev, 123);
417 /// # });
418 /// ```
419 #[cfg_attr(debug_assertions, track_caller)]
420 pub fn replace(self, new: T) -> T {
421 self.update(|val| std::mem::replace(val, new))
422 }
423
424 /// Silently gets the value of the signal and sets the new value to the default value.
425 ///
426 /// This is the silent version of [`Signal::take`].
427 #[cfg_attr(debug_assertions, track_caller)]
428 pub fn take_silent(self) -> T
429 where
430 T: Default,
431 {
432 self.replace_silent(T::default())
433 }
434
435 /// Gets the value of the signal and sets the new value to the default value.
436 ///
437 /// # Example
438 /// ```
439 /// # use sycamore_reactive::*;
440 /// # create_root(|| {
441 /// let state = create_signal(Some(123));
442 /// let prev = state.take();
443 /// assert_eq!(state.get(), None);
444 /// assert_eq!(prev, Some(123));
445 /// # });
446 /// ```
447 #[cfg_attr(debug_assertions, track_caller)]
448 pub fn take(self) -> T
449 where
450 T: Default,
451 {
452 self.replace(T::default())
453 }
454
455 /// Update the value of the signal silently. This will not trigger any updates in dependent
456 /// signals. As such, this is generally not recommended as it can easily lead to state
457 /// inconsistencies.
458 ///
459 /// This is the silent version of [`Signal::update`].
460 #[cfg_attr(debug_assertions, track_caller)]
461 pub fn update_silent<U>(self, f: impl FnOnce(&mut T) -> U) -> U {
462 let mut value = self
463 .get_mut()
464 .value
465 .take()
466 .expect("cannot update signal while reading");
467 let ret = f(value.downcast_mut().expect("wrong signal type"));
468 self.get_mut().value = Some(value);
469 ret
470 }
471
472 /// Update the value of the signal and automatically update any dependents.
473 ///
474 /// Using this has the advantage of not needing to clone the value when updating it, especially
475 /// with types that do not implement `Copy` where cloning can be expensive, or for types that
476 /// do not implement `Clone` at all.
477 ///
478 /// # Example
479 /// ```
480 /// # use sycamore_reactive::*;
481 /// # create_root(|| {
482 /// let state = create_signal("Hello".to_string());
483 /// state.update(|val| val.push_str(" Sycamore!"));
484 /// assert_eq!(state.get_clone(), "Hello Sycamore!");
485 /// # });
486 /// ```
487 #[cfg_attr(debug_assertions, track_caller)]
488 pub fn update<U>(self, f: impl FnOnce(&mut T) -> U) -> U {
489 let ret = self.update_silent(f);
490 self.0.root.propagate_updates(self.0.id);
491 ret
492 }
493
494 /// Use a function to produce a new value and sets the value silently.
495 ///
496 /// This is the silent version of [`Signal::set_fn`].
497 #[cfg_attr(debug_assertions, track_caller)]
498 pub fn set_fn_silent(self, f: impl FnOnce(&T) -> T) {
499 self.update_silent(move |val| *val = f(val));
500 }
501
502 /// Use a function to produce a new value and sets the value.
503 ///
504 /// # Example
505 /// ```
506 /// # use sycamore_reactive::*;
507 /// # create_root(|| {
508 /// let state = create_signal(123);
509 /// state.set_fn(|val| *val + 1);
510 /// assert_eq!(state.get(), 124);
511 /// # });
512 /// ```
513 #[cfg_attr(debug_assertions, track_caller)]
514 pub fn set_fn(self, f: impl FnOnce(&T) -> T) {
515 self.update(move |val| *val = f(val));
516 }
517
518 /// Split the signal into a reader/writer pair.
519 ///
520 /// # Example
521 /// ```
522 /// # use sycamore_reactive::*;
523 /// # create_root(|| {
524 /// let (read_signal, mut write_signal) = create_signal(0).split();
525 /// assert_eq!(read_signal.get(), 0);
526 /// write_signal(1);
527 /// assert_eq!(read_signal.get(), 1);
528 /// # });
529 /// ```
530 pub fn split(self) -> (ReadSignal<T>, impl Fn(T) -> T) {
531 (*self, move |value| self.replace(value))
532 }
533}
534
535/// We manually implement `Clone` + `Copy` for `Signal` so that we don't get extra bounds on `T`.
536impl<T> Clone for ReadSignal<T> {
537 fn clone(&self) -> Self {
538 *self
539 }
540}
541impl<T> Copy for ReadSignal<T> {}
542
543impl<T> Clone for Signal<T> {
544 fn clone(&self) -> Self {
545 *self
546 }
547}
548impl<T> Copy for Signal<T> {}
549
550// Implement `Default` for `ReadSignal` and `Signal`.
551impl<T: Default> Default for ReadSignal<T> {
552 fn default() -> Self {
553 *create_signal(Default::default())
554 }
555}
556impl<T: Default> Default for Signal<T> {
557 fn default() -> Self {
558 create_signal(Default::default())
559 }
560}
561
562// Forward `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `Hash` from inner type.
563impl<T: PartialEq> PartialEq for ReadSignal<T> {
564 fn eq(&self, other: &Self) -> bool {
565 self.with(|value| other.with(|other| value == other))
566 }
567}
568impl<T: Eq> Eq for ReadSignal<T> {}
569impl<T: PartialOrd> PartialOrd for ReadSignal<T> {
570 #[cfg_attr(debug_assertions, track_caller)]
571 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
572 self.with(|value| other.with(|other| value.partial_cmp(other)))
573 }
574}
575impl<T: Ord> Ord for ReadSignal<T> {
576 #[cfg_attr(debug_assertions, track_caller)]
577 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
578 self.with(|value| other.with(|other| value.cmp(other)))
579 }
580}
581impl<T: Hash> Hash for ReadSignal<T> {
582 #[cfg_attr(debug_assertions, track_caller)]
583 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
584 self.with(|value| value.hash(state))
585 }
586}
587
588impl<T: PartialEq> PartialEq for Signal<T> {
589 #[cfg_attr(debug_assertions, track_caller)]
590 fn eq(&self, other: &Self) -> bool {
591 self.with(|value| other.with(|other| value == other))
592 }
593}
594impl<T: Eq> Eq for Signal<T> {}
595impl<T: PartialOrd> PartialOrd for Signal<T> {
596 #[cfg_attr(debug_assertions, track_caller)]
597 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
598 self.with(|value| other.with(|other| value.partial_cmp(other)))
599 }
600}
601impl<T: Ord> Ord for Signal<T> {
602 #[cfg_attr(debug_assertions, track_caller)]
603 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
604 self.with(|value| other.with(|other| value.cmp(other)))
605 }
606}
607impl<T: Hash> Hash for Signal<T> {
608 #[cfg_attr(debug_assertions, track_caller)]
609 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
610 self.with(|value| value.hash(state))
611 }
612}
613
614impl<T> Deref for Signal<T> {
615 type Target = ReadSignal<T>;
616
617 fn deref(&self) -> &Self::Target {
618 &self.0
619 }
620}
621
622// Formatting implementations for `ReadSignal` and `Signal`.
623impl<T: fmt::Debug> fmt::Debug for ReadSignal<T> {
624 #[cfg_attr(debug_assertions, track_caller)]
625 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
626 self.with(|value| value.fmt(f))
627 }
628}
629impl<T: fmt::Debug> fmt::Debug for Signal<T> {
630 #[cfg_attr(debug_assertions, track_caller)]
631 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
632 self.with(|value| value.fmt(f))
633 }
634}
635
636impl<T: fmt::Display> fmt::Display for ReadSignal<T> {
637 #[cfg_attr(debug_assertions, track_caller)]
638 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
639 self.with(|value| value.fmt(f))
640 }
641}
642impl<T: fmt::Display> fmt::Display for Signal<T> {
643 #[cfg_attr(debug_assertions, track_caller)]
644 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
645 self.with(|value| value.fmt(f))
646 }
647}
648
649// Serde implementations for `ReadSignal` and `Signal`.
650#[cfg(feature = "serde")]
651impl<T: serde::Serialize> serde::Serialize for ReadSignal<T> {
652 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
653 self.with(|value| value.serialize(serializer))
654 }
655}
656#[cfg(feature = "serde")]
657impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for ReadSignal<T> {
658 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
659 Ok(*create_signal(T::deserialize(deserializer)?))
660 }
661}
662#[cfg(feature = "serde")]
663impl<T: serde::Serialize> serde::Serialize for Signal<T> {
664 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
665 self.with(|value| value.serialize(serializer))
666 }
667}
668#[cfg(feature = "serde")]
669impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for Signal<T> {
670 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
671 Ok(create_signal(T::deserialize(deserializer)?))
672 }
673}
674
675#[cfg(feature = "nightly")]
676impl<T: Copy> FnOnce<()> for ReadSignal<T> {
677 type Output = T;
678
679 extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
680 self.get()
681 }
682}
683
684impl<T: AddAssign<Rhs>, Rhs> AddAssign<Rhs> for Signal<T> {
685 fn add_assign(&mut self, rhs: Rhs) {
686 self.update(|this| *this += rhs);
687 }
688}
689impl<T: SubAssign<Rhs>, Rhs> SubAssign<Rhs> for Signal<T> {
690 fn sub_assign(&mut self, rhs: Rhs) {
691 self.update(|this| *this -= rhs);
692 }
693}
694impl<T: MulAssign<Rhs>, Rhs> MulAssign<Rhs> for Signal<T> {
695 fn mul_assign(&mut self, rhs: Rhs) {
696 self.update(|this| *this *= rhs);
697 }
698}
699impl<T: DivAssign<Rhs>, Rhs> DivAssign<Rhs> for Signal<T> {
700 fn div_assign(&mut self, rhs: Rhs) {
701 self.update(|this| *this /= rhs);
702 }
703}
704impl<T: RemAssign<Rhs>, Rhs> RemAssign<Rhs> for Signal<T> {
705 fn rem_assign(&mut self, rhs: Rhs) {
706 self.update(|this| *this %= rhs);
707 }
708}
709
710// We need to implement this again for `Signal` despite `Signal` deref-ing to `ReadSignal` since
711// we also have another implementation of `FnOnce` for `Signal`.
712#[cfg(feature = "nightly")]
713impl<T: Copy> FnOnce<()> for Signal<T> {
714 type Output = T;
715
716 extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
717 self.get()
718 }
719}
720
721#[cfg(feature = "nightly")]
722impl<T: Copy> FnOnce<(T,)> for Signal<T> {
723 type Output = T;
724
725 extern "rust-call" fn call_once(self, (val,): (T,)) -> Self::Output {
726 self.replace(val)
727 }
728}
729
730#[cfg(test)]
731mod tests {
732 use crate::*;
733
734 #[test]
735 fn signal() {
736 let _ = create_root(|| {
737 let state = create_signal(0);
738 assert_eq!(state.get(), 0);
739
740 state.set(1);
741 assert_eq!(state.get(), 1);
742
743 state.set_fn(|n| *n + 1);
744 assert_eq!(state.get(), 2);
745 });
746 }
747
748 #[test]
749 fn signal_composition() {
750 let _ = create_root(|| {
751 let state = create_signal(0);
752 let double = || state.get() * 2;
753
754 assert_eq!(double(), 0);
755 state.set(1);
756 assert_eq!(double(), 2);
757 });
758 }
759
760 #[test]
761 fn set_silent_signal() {
762 let _ = create_root(|| {
763 let state = create_signal(0);
764 let double = state.map(|&x| x * 2);
765
766 assert_eq!(double.get(), 0);
767 state.set_silent(1);
768 assert_eq!(double.get(), 0); // double value is unchanged.
769
770 state.set_fn_silent(|n| n + 1);
771 assert_eq!(double.get(), 0); // double value is unchanged.
772 });
773 }
774
775 #[test]
776 fn read_signal() {
777 let _ = create_root(|| {
778 let state = create_signal(0);
779 let readonly: ReadSignal<i32> = *state;
780
781 assert_eq!(readonly.get(), 0);
782 state.set(1);
783 assert_eq!(readonly.get(), 1);
784 });
785 }
786
787 #[test]
788 fn map_signal() {
789 let _ = create_root(|| {
790 let state = create_signal(0);
791 let double = state.map(|&x| x * 2);
792
793 assert_eq!(double.get(), 0);
794 state.set(1);
795 assert_eq!(double.get(), 2);
796 });
797 }
798
799 #[test]
800 fn take_signal() {
801 let _ = create_root(|| {
802 let state = create_signal(123);
803
804 let x = state.take();
805 assert_eq!(x, 123);
806 assert_eq!(state.get(), 0);
807 });
808 }
809
810 #[test]
811 fn take_silent_signal() {
812 let _ = create_root(|| {
813 let state = create_signal(123);
814 let double = state.map(|&x| x * 2);
815
816 // Do not trigger subscribers.
817 state.take_silent();
818 assert_eq!(state.get(), 0);
819 assert_eq!(double.get(), 246);
820 });
821 }
822
823 #[test]
824 fn signal_split() {
825 let _ = create_root(|| {
826 let (state, set_state) = create_signal(0).split();
827 assert_eq!(state.get(), 0);
828
829 set_state(1);
830 assert_eq!(state.get(), 1);
831 });
832 }
833
834 #[test]
835 fn signal_display() {
836 let _ = create_root(|| {
837 let signal = create_signal(0);
838 assert_eq!(format!("{signal}"), "0");
839 let read_signal: ReadSignal<_> = *signal;
840 assert_eq!(format!("{read_signal}"), "0");
841 let memo = create_memo(|| 0);
842 assert_eq!(format!("{memo}"), "0");
843 });
844 }
845
846 #[test]
847 fn signal_debug() {
848 let _ = create_root(|| {
849 let signal = create_signal(0);
850 assert_eq!(format!("{signal:?}"), "0");
851 let read_signal: ReadSignal<_> = *signal;
852 assert_eq!(format!("{read_signal:?}"), "0");
853 let memo = create_memo(|| 0);
854 assert_eq!(format!("{memo:?}"), "0");
855 });
856 }
857
858 #[test]
859 fn signal_add_assign_update() {
860 let _ = create_root(|| {
861 let mut signal = create_signal(0);
862 let counter = create_signal(0);
863 create_effect(move || {
864 signal.track();
865 counter.set(counter.get_untracked() + 1);
866 });
867 signal += 1;
868 signal -= 1;
869 signal *= 1;
870 signal /= 1;
871 assert_eq!(counter.get(), 5);
872 });
873 }
874
875 #[test]
876 fn signal_update() {
877 let _ = create_root(|| {
878 let signal = create_signal("Hello ".to_string());
879 let counter = create_signal(0);
880 create_effect(move || {
881 signal.track();
882 counter.set(counter.get_untracked() + 1);
883 });
884 signal.update(|value| value.push_str("World!"));
885 assert_eq!(signal.get_clone(), "Hello World!");
886 assert_eq!(counter.get(), 2);
887 });
888 }
889}