maple_core/reactive/
signal.rs

1use super::*;
2use std::cell::RefCell;
3use std::collections::HashSet;
4use std::fmt;
5use std::ops::Deref;
6use std::rc::Rc;
7
8/// A readonly [`Signal`].
9///
10/// Returned by functions that provide a handle to access state.
11/// Use [`Signal::handle`] or [`Signal::into_handle`] to retrieve a handle from a [`Signal`].
12pub struct StateHandle<T: 'static>(Rc<RefCell<SignalInner<T>>>);
13
14impl<T: 'static> StateHandle<T> {
15    /// Get the current value of the state.
16    pub fn get(&self) -> Rc<T> {
17        // if inside an effect, add this signal to dependency list
18        CONTEXTS.with(|contexts| {
19            if let Some(last_context) = contexts.borrow().last() {
20                let signal = Rc::downgrade(&self.0);
21
22                last_context
23                    .upgrade()
24                    .expect("Running should be valid while inside reactive scope")
25                    .borrow_mut()
26                    .as_mut()
27                    .unwrap()
28                    .dependencies
29                    .insert(Dependency(signal));
30            }
31        });
32
33        self.get_untracked()
34    }
35
36    /// Get the current value of the state, without tracking this as a dependency if inside a
37    /// reactive context.
38    ///
39    /// # Example
40    ///
41    /// ```
42    /// use maple_core::prelude::*;
43    ///
44    /// let state = Signal::new(1);
45    ///
46    /// let double = create_memo({
47    ///     let state = state.clone();
48    ///     move || *state.get_untracked() * 2
49    /// });
50    ///
51    /// assert_eq!(*double.get(), 2);
52    ///
53    /// state.set(2);
54    /// // double value should still be old value because state was untracked
55    /// assert_eq!(*double.get(), 2);
56    /// ```
57    pub fn get_untracked(&self) -> Rc<T> {
58        Rc::clone(&self.0.borrow().inner)
59    }
60}
61
62impl<T: 'static> Clone for StateHandle<T> {
63    fn clone(&self) -> Self {
64        Self(Rc::clone(&self.0))
65    }
66}
67
68impl<T: fmt::Debug> fmt::Debug for StateHandle<T> {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        f.debug_tuple("StateHandle")
71            .field(&self.get_untracked())
72            .finish()
73    }
74}
75
76#[cfg(feature = "serde")]
77impl<T: serde::Serialize> serde::Serialize for StateHandle<T> {
78    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
79    where
80        S: serde::Serializer,
81    {
82        self.get_untracked().as_ref().serialize(serializer)
83    }
84}
85
86#[cfg(feature = "serde")]
87impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for StateHandle<T> {
88    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
89    where
90        D: serde::Deserializer<'de>,
91    {
92        Ok(Signal::new(T::deserialize(deserializer)?).handle())
93    }
94}
95
96/// State that can be set.
97///
98/// # Example
99/// ```
100/// use maple_core::prelude::*;
101///
102/// let state = Signal::new(0);
103/// assert_eq!(*state.get(), 0);
104///
105/// state.set(1);
106/// assert_eq!(*state.get(), 1);
107/// ```
108pub struct Signal<T: 'static> {
109    handle: StateHandle<T>,
110}
111
112impl<T: 'static> Signal<T> {
113    /// Creates a new signal with the given value.
114    ///
115    /// # Example
116    /// ```
117    /// # use maple_core::prelude::*;
118    /// let state = Signal::new(0);
119    /// # assert_eq!(*state.get(), 0);
120    /// ```
121    pub fn new(value: T) -> Self {
122        Self {
123            handle: StateHandle(Rc::new(RefCell::new(SignalInner::new(value)))),
124        }
125    }
126
127    /// Set the current value of the state.
128    ///
129    /// This will notify and update any effects and memos that depend on this value.
130    ///
131    /// # Example
132    /// ```
133    /// # use maple_core::prelude::*;
134    ///
135    /// let state = Signal::new(0);
136    /// assert_eq!(*state.get(), 0);
137    ///
138    /// state.set(1);
139    /// assert_eq!(*state.get(), 1);
140    /// ```
141    pub fn set(&self, new_value: T) {
142        self.handle.0.borrow_mut().update(new_value);
143
144        self.trigger_subscribers();
145    }
146
147    /// Get the [`StateHandle`] associated with this signal.
148    ///
149    /// This is a shortcut for `(*signal).clone()`.
150    pub fn handle(&self) -> StateHandle<T> {
151        self.handle.clone()
152    }
153
154    /// Consumes this signal and returns its underlying [`StateHandle`].
155    pub fn into_handle(self) -> StateHandle<T> {
156        self.handle
157    }
158
159    /// Calls all the subscribers without modifying the state.
160    /// This can be useful when using patterns such as inner mutability where the state updated will not be automatically triggered.
161    /// In the general case, however, it is preferable to use [`Signal::set`] instead.
162    pub fn trigger_subscribers(&self) {
163        // Clone subscribers to prevent modifying list when calling callbacks.
164        let subscribers = self.handle.0.borrow().subscribers.clone();
165
166        for subscriber in subscribers {
167            // subscriber might have already been destroyed in the case of nested effects
168            if let Some(callback) = subscriber.try_callback() {
169                callback()
170            }
171        }
172    }
173}
174
175impl<T: 'static> Deref for Signal<T> {
176    type Target = StateHandle<T>;
177
178    fn deref(&self) -> &Self::Target {
179        &self.handle
180    }
181}
182
183impl<T: 'static> Clone for Signal<T> {
184    fn clone(&self) -> Self {
185        Self {
186            handle: self.handle.clone(),
187        }
188    }
189}
190
191impl<T: PartialEq> PartialEq for Signal<T> {
192    fn eq(&self, other: &Signal<T>) -> bool {
193        self.get_untracked().eq(&other.get_untracked())
194    }
195}
196
197impl<T: fmt::Debug> fmt::Debug for Signal<T> {
198    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199        f.debug_tuple("Signal")
200            .field(&self.get_untracked())
201            .finish()
202    }
203}
204
205#[cfg(feature = "serde")]
206impl<T: serde::Serialize> serde::Serialize for Signal<T> {
207    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
208    where
209        S: serde::Serializer,
210    {
211        self.get_untracked().as_ref().serialize(serializer)
212    }
213}
214
215#[cfg(feature = "serde")]
216impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for Signal<T> {
217    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
218    where
219        D: serde::Deserializer<'de>,
220    {
221        Ok(Signal::new(T::deserialize(deserializer)?))
222    }
223}
224
225pub(super) struct SignalInner<T> {
226    inner: Rc<T>,
227    subscribers: HashSet<Callback>,
228}
229
230impl<T> SignalInner<T> {
231    fn new(value: T) -> Self {
232        Self {
233            inner: Rc::new(value),
234            subscribers: HashSet::new(),
235        }
236    }
237
238    /// Adds a handler to the subscriber list. If the handler is already a subscriber, does nothing.
239    fn subscribe(&mut self, handler: Callback) {
240        self.subscribers.insert(handler);
241    }
242
243    /// Removes a handler from the subscriber list. If the handler is not a subscriber, does nothing.
244    fn unsubscribe(&mut self, handler: &Callback) {
245        self.subscribers.remove(handler);
246    }
247
248    /// Updates the inner value. This does **NOT** call the subscribers.
249    /// You will have to do so manually with `trigger_subscribers`.
250    fn update(&mut self, new_value: T) {
251        self.inner = Rc::new(new_value);
252    }
253}
254
255/// Trait for any [`SignalInner`], regardless of type param `T`.
256pub(super) trait AnySignalInner {
257    /// Wrapper around [`SignalInner::subscribe`].
258    fn subscribe(&self, handler: Callback);
259    /// Wrapper around [`SignalInner::unsubscribe`].
260    fn unsubscribe(&self, handler: &Callback);
261}
262
263impl<T> AnySignalInner for RefCell<SignalInner<T>> {
264    fn subscribe(&self, handler: Callback) {
265        self.borrow_mut().subscribe(handler);
266    }
267
268    fn unsubscribe(&self, handler: &Callback) {
269        self.borrow_mut().unsubscribe(handler);
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use super::*;
276
277    #[test]
278    fn signals() {
279        let state = Signal::new(0);
280        assert_eq!(*state.get(), 0);
281
282        state.set(1);
283        assert_eq!(*state.get(), 1);
284    }
285
286    #[test]
287    fn signal_composition() {
288        let state = Signal::new(0);
289
290        let double = || *state.get() * 2;
291
292        assert_eq!(double(), 0);
293
294        state.set(1);
295        assert_eq!(double(), 2);
296    }
297
298    #[test]
299    fn state_handle() {
300        let state = Signal::new(0);
301        let readonly = state.handle();
302
303        assert_eq!(*readonly.get(), 0);
304
305        state.set(1);
306        assert_eq!(*readonly.get(), 1);
307    }
308}