Skip to main content

telar_reactive_core/
signal.rs

1use std::marker::PhantomData;
2
3use crate::runtime::{self, SignalId};
4
5fn read_with<T: 'static, R>(id: SignalId, f: impl FnOnce(&T) -> R) -> R {
6    runtime::track_signal(id);
7    runtime::with_signal_value::<T, R>(id, f)
8}
9
10fn peek_with<T: 'static, R>(id: SignalId, f: impl FnOnce(&T) -> R) -> R {
11    runtime::with_signal_value::<T, R>(id, f)
12}
13
14pub struct ReadSignal<T: 'static> {
15    pub(crate) id: SignalId,
16    _marker: PhantomData<T>,
17}
18
19impl<T: 'static> Clone for ReadSignal<T> {
20    fn clone(&self) -> Self {
21        runtime::clone_signal(self.id);
22        ReadSignal {
23            id: self.id,
24            _marker: PhantomData,
25        }
26    }
27}
28
29impl<T: 'static> Drop for ReadSignal<T> {
30    fn drop(&mut self) {
31        runtime::drop_signal(self.id);
32    }
33}
34
35impl<T: Clone + 'static> ReadSignal<T> {
36    pub fn get(&self) -> T {
37        read_with::<T, T>(self.id, |v| v.clone())
38    }
39
40    pub fn peek(&self) -> T {
41        peek_with::<T, T>(self.id, |v| v.clone())
42    }
43}
44
45impl<T: 'static> ReadSignal<T> {
46    pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
47        read_with::<T, R>(self.id, f)
48    }
49}
50
51pub struct RwSignal<T: 'static> {
52    pub(crate) id: SignalId,
53    _marker: PhantomData<T>,
54}
55
56impl<T: 'static> Clone for RwSignal<T> {
57    fn clone(&self) -> Self {
58        runtime::clone_signal(self.id);
59        RwSignal {
60            id: self.id,
61            _marker: PhantomData,
62        }
63    }
64}
65
66impl<T: 'static> Drop for RwSignal<T> {
67    fn drop(&mut self) {
68        runtime::drop_signal(self.id);
69    }
70}
71
72impl<T: 'static> RwSignal<T> {
73    pub fn set(&self, value: T) {
74        runtime::set_signal_value(self.id, value);
75        runtime::notify_signal(self.id);
76    }
77
78    pub fn update(&self, f: impl FnOnce(&mut T)) {
79        runtime::update_signal_value::<T>(self.id, f);
80        runtime::notify_signal(self.id);
81    }
82
83    pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
84        read_with::<T, R>(self.id, f)
85    }
86
87    /// As [`with`](Self::with), but does not subscribe the caller — for reads from an event handler, where
88    /// tracking would attach the value to whatever effect happens to be running.
89    pub fn peek_with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
90        peek_with::<T, R>(self.id, f)
91    }
92
93    pub fn read_only(&self) -> ReadSignal<T> {
94        runtime::clone_signal(self.id);
95        ReadSignal {
96            id: self.id,
97            _marker: PhantomData,
98        }
99    }
100}
101
102impl<T: Clone + 'static> RwSignal<T> {
103    pub fn get(&self) -> T {
104        read_with::<T, T>(self.id, |v| v.clone())
105    }
106
107    pub fn peek(&self) -> T {
108        peek_with::<T, T>(self.id, |v| v.clone())
109    }
110}
111
112impl RwSignal<bool> {
113    /// Flip a boolean signal in place — sugar for `.update(|v| *v = !*v)`, so `$flag.toggle()` reads cleanly.
114    pub fn toggle(&self) {
115        self.update(|v| *v = !*v);
116    }
117}
118
119pub fn signal<T: 'static>(value: T) -> RwSignal<T> {
120    let id = runtime::create_signal_storage(value, 1);
121    RwSignal {
122        id,
123        _marker: PhantomData,
124    }
125}