reactive_signals/signals/
signal_accessors.rs

1use crate::runtimes::Runtime;
2
3use super::{
4    updater::propagate_change, Modifiable, OptReadable, Readable, Signal, SignalId, SignalInner,
5    SignalType,
6};
7
8impl<T, RT> Signal<T, RT>
9where
10    T: SignalType + Modifiable,
11    RT: Runtime,
12{
13    /// Set the signal's value and notifies subscribers
14    /// if the value changed when it implements `PartialEq`
15    /// otherwise it always notifies.
16    pub fn set(&self, val: T::Inner) {
17        self.id.rt_ref(|rt| {
18            let is_equal = rt[self.id].with_signal(self.id, |sig| sig.value().set::<T>(val));
19            if !is_equal {
20                propagate_change(rt, self.id);
21            }
22        });
23    }
24
25    /// Applies a function to the current value to mutate it in place and returns
26    /// whatever that function returns.
27    ///
28    /// Subscribers are notified if the value changed when it implements [PartialEq]
29    /// otherwise it always notifies.
30    ///
31    /// **Example of using the return value**
32    ///
33    /// ```rust
34    /// # use reactive_signals::{signal, runtimes::ClientRuntime};
35    /// # let sc = ClientRuntime::new_root_scope();
36    /// let count = signal!(sc, 2);
37    /// let is_even = count.update(|val| {
38    ///     *val += 1;
39    ///     *val % 2 == 0
40    /// });
41    /// ```
42    ///
43    pub fn update<R: 'static>(&self, f: impl Fn(&mut T::Inner) -> R) -> R {
44        self.id.rt_ref(|rt| {
45            let (is_equal, r) =
46                rt[self.id].with_signal(self.id, |sig| sig.value().update::<T, R>(f));
47            if !is_equal {
48                propagate_change(rt, self.id);
49            }
50            r
51        })
52    }
53}
54
55impl<T, RT> Signal<T, RT>
56where
57    T: SignalType + Readable,
58    T::Inner: Copy,
59    RT: Runtime,
60{
61    /// Get a copy of the signal value (if the value implements [Copy])
62    pub fn get(&self) -> T::Inner {
63        register_and_run(self.id, |sig| sig.value().get::<T>())
64    }
65}
66
67impl<T, RT> Signal<T, RT>
68where
69    T: SignalType + Readable,
70    T::Inner: Clone,
71    RT: Runtime,
72{
73    /// Get a clone of the signal value (if the value implements [Clone])
74    ///
75    /// Use the `.with()` function if you can in order to avoid the clone.
76    pub fn cloned(&self) -> T::Inner {
77        register_and_run(self.id, |sig| sig.value().cloned::<T>())
78    }
79}
80
81impl<T, RT> Signal<T, RT>
82where
83    T: SignalType + Readable,
84    RT: Runtime,
85{
86    /// Applies a function to the current value to mutate it in place and returns
87    /// whatever that function returns.
88    ///
89    /// Subscribers are notified if the value changed when it implements `PartialEq`
90    /// otherwise it always notifies.
91    ///
92    /// **Example of using the return value**
93    ///
94    /// ```rust
95    /// # use reactive_signals::{signal, runtimes::ClientRuntime};
96    /// # let sc = ClientRuntime::new_root_scope();
97    /// let count = signal!(sc, 2);
98    /// let is_even = count.with(|val| *val % 2 == 0);
99    /// ```
100    ///
101    pub fn with<R: 'static>(&self, f: impl Fn(&T::Inner) -> R) -> R {
102        register_and_run(self.id, |sig| sig.value().with::<T, R>(f))
103    }
104}
105
106impl<T, RT> Signal<T, RT>
107where
108    T: SignalType + OptReadable,
109    RT: Runtime,
110{
111    const SHOULD_RUN: bool =
112        (RT::IS_SERVER && T::RUN_ON_SERVER) || (!RT::IS_SERVER && T::RUN_ON_CLIENT);
113}
114impl<T, RT> Signal<T, RT>
115where
116    T: SignalType + OptReadable,
117    T::Inner: Copy + Default,
118    RT: Runtime,
119{
120    /// Get a copy of the signal value (if the value implements [Copy])
121    pub fn opt_get(&self) -> Option<T::Inner> {
122        Self::SHOULD_RUN.then(|| register_and_run(self.id, |sig| sig.value().get::<T>()))
123    }
124}
125
126impl<T, RT> Signal<T, RT>
127where
128    T: SignalType + OptReadable,
129    T::Inner: Clone,
130    RT: Runtime,
131{
132    /// Get a clone of the signal value (if the value implements [Clone])
133    ///
134    /// Use the `.with()` function if you can in order to avoid the clone.
135    pub fn opt_cloned(&self) -> Option<T::Inner> {
136        Self::SHOULD_RUN.then(|| register_and_run(self.id, |sig| sig.value().cloned::<T>()))
137    }
138}
139
140impl<T, RT> Signal<T, RT>
141where
142    T: SignalType + OptReadable,
143    RT: Runtime,
144{
145    /// Applies a function to the current value to mutate it in place and returns
146    /// whatever that function returns.
147    ///
148    /// Subscribers are notified if the value changed when it implements `PartialEq`
149    /// otherwise it always notifies.
150    ///
151    /// **Example of using the return value**
152    ///
153    /// ```rust
154    /// # use reactive_signals::{signal, runtimes::ClientRuntime};
155    /// # let sc = ClientRuntime::new_root_scope();
156    /// let count = signal!(sc, 2);
157    /// let is_even = count.with(|val| *val % 2 == 0);
158    /// ```
159    ///
160    pub fn opt_with<R: 'static>(&self, f: impl Fn(&T::Inner) -> R) -> Option<R> {
161        Self::SHOULD_RUN.then(|| register_and_run(self.id, |sig| sig.value().with::<T, R>(f)))
162    }
163}
164
165#[inline]
166fn register_and_run<RT: Runtime, T: 'static, F: FnOnce(&SignalInner<RT>) -> T>(
167    id: SignalId<RT>,
168    f: F,
169) -> T {
170    id.rt_ref(|rt| {
171        if let Some(listener) = rt.get_running_signal() {
172            rt[id].with_signal(id, |signal| {
173                signal.listeners.insert(listener);
174                f(signal)
175            })
176        } else {
177            rt[id].with_signal(id, |signal| f(signal))
178        }
179    })
180}