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}