silkenweb_reactive/
signal.rs

1//! Signals are like variables that update their dependencies.
2use std::{
3    cell::{Ref, RefCell, RefMut},
4    collections::HashSet,
5    hash::Hash,
6    rc::{self, Rc},
7};
8
9use crate::clone;
10
11type SharedState<T> = Rc<State<T>>;
12type WeakSharedState<T> = rc::Weak<State<T>>;
13
14/// A [`Signal`] is like a varible, but it can update it's dependencies when it
15/// changes.
16///
17/// ```
18/// # use silkenweb_reactive::signal::*;
19/// let x = Signal::new(0);
20/// let next_x = x.read().map(|x| x + 1);
21/// assert_eq!(*next_x.current(), 1);
22/// x.write().set(2);
23/// assert_eq!(*next_x.current(), 3);
24/// ```
25pub struct Signal<T>(SharedState<T>);
26
27impl<T> Clone for Signal<T> {
28    fn clone(&self) -> Self {
29        Self(self.0.clone())
30    }
31}
32
33impl<T: 'static> Signal<T> {
34    pub fn new(initial: T) -> Self {
35        Self(Rc::new(State::new(initial)))
36    }
37
38    pub fn read(&self) -> ReadSignal<T> {
39        ReadSignal(self.0.clone())
40    }
41
42    pub fn write(&self) -> WriteSignal<T> {
43        WriteSignal(Rc::downgrade(&self.0))
44    }
45}
46
47/// Receive changes from a signal.
48///
49/// Changes will stop being received when this is destroyed:
50///
51/// ```
52/// # use silkenweb_reactive::{clone, signal::*};
53/// # use std::{mem, cell::Cell, rc::Rc};
54/// let x = Signal::new(1);
55/// let seen_by_y = Rc::new(Cell::new(0));
56/// let y = x.read().map({
57///     clone!(seen_by_y);
58///     move|&x| seen_by_y.set(x)
59/// });
60/// assert_eq!(seen_by_y.get(), 1);
61/// x.write().set(2);
62/// assert_eq!(seen_by_y.get(), 2);
63/// mem::drop(y);
64/// // We won't see this update
65/// x.write().set(3);
66/// assert_eq!(seen_by_y.get(), 2);
67/// ```
68pub struct ReadSignal<T>(SharedState<T>);
69
70impl<T> Clone for ReadSignal<T> {
71    fn clone(&self) -> Self {
72        Self(self.0.clone())
73    }
74}
75
76impl<T: 'static> ReadSignal<T> {
77    /// The current value of the signal
78    pub fn current(&self) -> Ref<T> {
79        self.0.current()
80    }
81
82    /// Only propagate actual changes to the signal value.
83    ///
84    /// ```
85    /// # use silkenweb_reactive::{clone, signal::*};
86    /// # use std::{mem, cell::Cell, rc::Rc};
87    /// let all_updates_count = Rc::new(Cell::new(0));
88    /// let only_changes_count = Rc::new(Cell::new(0));
89    /// let x = Signal::new(0);
90    /// let all_updates = x.read().map({
91    ///     clone!(all_updates_count);
92    ///     move |_| all_updates_count.set(all_updates_count.get() + 1)
93    /// });
94    /// let only_changes = x.read().only_changes().map({
95    ///     clone!(only_changes_count);
96    ///     move |_| only_changes_count.set(only_changes_count.get() + 1)
97    /// });
98    ///
99    /// x.write().set(1);
100    /// x.write().set(1);
101    /// assert_eq!(all_updates_count.get(), 3, "One for init + 2 updates");
102    /// assert_eq!(only_changes_count.get(), 2, "One for init + 1 actual change");
103    /// ```
104    pub fn only_changes(&self) -> ReadSignal<T>
105    where
106        T: Clone + Eq,
107    {
108        let child = Signal::new(self.current().clone());
109
110        self.add_dependent(
111            &child,
112            Rc::new({
113                clone!(child);
114
115                move |new_value| {
116                    if *child.read().current() != *new_value {
117                        child.write().set(new_value.clone())
118                    }
119                }
120            }),
121        );
122
123        child.read()
124    }
125
126    /// Map a function onto the inner value to produce a new [`ReadSignal`].
127    ///
128    /// This only exists to make type inference easier, and just forwards its
129    /// arguments to [`map_to`](Self::map_to).
130    pub fn map<Output, Generate>(&self, generate: Generate) -> ReadSignal<Output>
131    where
132        Output: 'static,
133        Generate: 'static + Fn(&T) -> Output,
134    {
135        self.map_to(generate)
136    }
137
138    /// Receive changes to a signal.
139    pub fn map_to<Output>(&self, receiver: impl SignalReceiver<T, Output>) -> ReadSignal<Output>
140    where
141        Output: 'static,
142    {
143        let child = Signal::new(receiver.receive(&self.current()));
144
145        self.add_dependent(
146            &child,
147            Rc::new({
148                let set_value = child.write();
149
150                move |new_value| set_value.set(receiver.receive(new_value))
151            }),
152        );
153
154        child.read()
155    }
156
157    fn add_dependent<U>(&self, child: &Signal<U>, dependent_callback: Rc<dyn Fn(&T)>) {
158        self.0
159            .dependents
160            .borrow_mut()
161            .insert(DependentCallback::new(&dependent_callback));
162        child
163            .0
164            .parents
165            .borrow_mut()
166            .push(Box::new(Parent::new(dependent_callback, &self)));
167    }
168}
169
170/// Receive changes to a signal.
171///
172/// Pass a `SignalReceiver` to [`ReadSignal::map_to`], as an alternative to
173/// passing a closure to [`ReadSignal::map`].
174pub trait SignalReceiver<Input, Output>: 'static
175where
176    Input: 'static,
177    Output: 'static,
178{
179    fn receive(&self, x: &Input) -> Output;
180}
181
182impl<Input, Output, F> SignalReceiver<Input, Output> for F
183where
184    Input: 'static,
185    Output: 'static,
186    F: 'static + Fn(&Input) -> Output,
187{
188    fn receive(&self, x: &Input) -> Output {
189        self(x)
190    }
191}
192
193/// Write changes to a signal.
194pub struct WriteSignal<T>(WeakSharedState<T>);
195
196impl<T> Clone for WriteSignal<T> {
197    fn clone(&self) -> Self {
198        Self(self.0.clone())
199    }
200}
201
202impl<T: 'static> WriteSignal<T> {
203    /// Set the inner value of a signal, and update downstream signals.
204    pub fn set(&self, new_value: T) {
205        if let Some(state) = self.0.upgrade() {
206            *state.current_mut() = new_value;
207            state.update_dependents();
208        }
209    }
210
211    /// Replace inner value of a signal using `f`, and update downstream
212    /// signals.
213    pub fn replace(&self, f: impl 'static + FnOnce(&T) -> T) {
214        self.mutate(|x| *x = f(x));
215    }
216
217    /// Mutate the inner value of a signal using `f`, and update downstream
218    /// signals.
219    pub fn mutate(&self, f: impl 'static + FnOnce(&mut T)) {
220        if let Some(state) = self.0.upgrade() {
221            f(&mut state.current_mut());
222            state.update_dependents();
223        }
224    }
225}
226
227/// Zip signals together to create a new one. The tuple implementation allows
228/// you to write `(signal0, signal1).map(...)`.
229pub trait ZipSignal<Generate> {
230    /// The inner type of the target signal.
231    type Target;
232
233    /// Map `generate` over the inner signal value.
234    fn map(&self, generate: Generate) -> ReadSignal<Self::Target>;
235}
236
237impl<T0, T1, U, Generate> ZipSignal<Generate> for (ReadSignal<T0>, ReadSignal<T1>)
238where
239    T0: 'static,
240    T1: 'static,
241    U: 'static,
242    Generate: 'static + Fn(&T0, &T1) -> U,
243{
244    type Target = U;
245
246    fn map(&self, generate: Generate) -> ReadSignal<Self::Target> {
247        let x0 = &self.0;
248        let x1 = &self.1;
249        let child = Signal::new(generate(&x0.current(), &x1.current()));
250        let generate0 = Rc::new(generate);
251        let generate1 = generate0.clone();
252
253        x0.add_dependent(
254            &child,
255            Rc::new({
256                let set_value = child.write();
257                clone!(x1);
258
259                move |new_value| set_value.set(generate0(new_value, &x1.current()))
260            }),
261        );
262
263        x1.add_dependent(
264            &child,
265            Rc::new({
266                let set_value = child.write();
267                clone!(x0);
268
269                move |new_value| set_value.set(generate1(&x0.current(), new_value))
270            }),
271        );
272
273        child.read()
274    }
275}
276
277struct State<T> {
278    current: RefCell<T>,
279    parents: RefCell<Vec<Box<dyn AnyParent>>>,
280    dependents: RefCell<HashSet<DependentCallback<T>>>,
281}
282
283impl<T: 'static> State<T> {
284    fn new(init: T) -> Self {
285        Self {
286            current: RefCell::new(init),
287            parents: RefCell::new(Vec::new()),
288            dependents: RefCell::new(HashSet::new()),
289        }
290    }
291
292    fn update_dependents(&self) {
293        // Take a copy here, as updating dependencies could add or remove dependencies
294        // here, causing a multiple borrows.
295        let dependents = self.dependents.borrow().clone();
296
297        // If a dependency is added while updating, it won't need updating because it
298        // will be initialized with the current value.
299        //
300        // If a dependency is removed before we get to it in the loop, we'll have a null
301        // weak reference and ignore it.
302        //
303        // If a dependency is removed after we get to it in the loop, we'll still update
304        // it.
305        for dep in &dependents {
306            if let Some(f) = dep.0.upgrade() {
307                f(&self.current());
308            }
309        }
310    }
311
312    fn current(&self) -> Ref<T> {
313        self.current
314            .try_borrow()
315            .expect("Possible circular dependency")
316    }
317
318    fn current_mut(&self) -> RefMut<T> {
319        self.current
320            .try_borrow_mut()
321            .expect("Possible circular dependency")
322    }
323}
324
325trait AnyParent {}
326
327struct Parent<T> {
328    dependent_callback: Rc<dyn Fn(&T)>,
329    parent: Rc<State<T>>,
330}
331
332impl<T> Parent<T> {
333    fn new(dependent_callback: Rc<dyn Fn(&T)>, parent: &ReadSignal<T>) -> Self {
334        Self {
335            dependent_callback,
336            parent: parent.0.clone(),
337        }
338    }
339}
340
341impl<T> AnyParent for Parent<T> {}
342
343impl<T> Drop for Parent<T> {
344    fn drop(&mut self) {
345        let removed = self
346            .parent
347            .dependents
348            .borrow_mut()
349            .remove(&DependentCallback(Rc::downgrade(&self.dependent_callback)));
350        assert!(removed);
351    }
352}
353
354struct DependentCallback<T>(rc::Weak<dyn Fn(&T)>);
355
356impl<T> Clone for DependentCallback<T> {
357    fn clone(&self) -> Self {
358        Self(self.0.clone())
359    }
360}
361
362impl<T> DependentCallback<T> {
363    fn new(f: &Rc<dyn 'static + Fn(&T)>) -> Self {
364        Self(Rc::downgrade(f))
365    }
366
367    fn upgrade(&self) -> Rc<dyn 'static + Fn(&T)> {
368        self.0.upgrade().unwrap()
369    }
370}
371
372impl<T> PartialEq for DependentCallback<T> {
373    fn eq(&self, other: &Self) -> bool {
374        // We need to discard the vtable by casting as we only care if the data pointers
375        // are equal. See https://github.com/rust-lang/rust/issues/46139
376        Rc::as_ptr(&self.upgrade()).cast::<()>() == Rc::as_ptr(&other.upgrade()).cast::<()>()
377    }
378}
379
380impl<T> Eq for DependentCallback<T> {}
381
382impl<T> Hash for DependentCallback<T> {
383    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
384        Rc::as_ptr(&self.upgrade()).hash(state);
385    }
386}