floem_reactive/
signal.rs

1use std::{
2    any::Any,
3    cell::{Ref, RefCell},
4    collections::HashMap,
5    fmt,
6    marker::PhantomData,
7    rc::Rc,
8};
9
10use crate::{
11    effect::{run_effect, EffectTrait},
12    id::Id,
13    read::{SignalRead, SignalTrack, SignalWith},
14    runtime::RUNTIME,
15    write::SignalWrite,
16    SignalGet, SignalUpdate,
17};
18
19/// Marker type explaining why something can't be sent across threads
20/// If you need to synchronize data into a signal from another thread, use `create_signal_from_channel` or a similar constructor.
21#[allow(dead_code)]
22pub struct NotThreadSafe(*const ());
23
24/// A read write Signal which can act as both a Getter and a Setter
25pub struct RwSignal<T> {
26    pub(crate) id: Id,
27    pub(crate) ty: PhantomData<T>,
28    pub(crate) ts: PhantomData<NotThreadSafe>,
29}
30
31impl<T> Copy for RwSignal<T> {}
32
33impl<T> Clone for RwSignal<T> {
34    fn clone(&self) -> Self {
35        *self
36    }
37}
38
39impl<T> Eq for RwSignal<T> {}
40
41impl<T> PartialEq for RwSignal<T> {
42    fn eq(&self, other: &Self) -> bool {
43        self.id == other.id
44    }
45}
46
47impl<T> fmt::Debug for RwSignal<T> {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        let mut s = f.debug_struct("RwSignal");
50        s.field("id", &self.id);
51        s.field("ty", &self.ty);
52        s.finish()
53    }
54}
55
56impl<T> RwSignal<T> {
57    /// Create a Getter of this Signal
58    pub fn read_only(&self) -> ReadSignal<T> {
59        ReadSignal {
60            id: self.id,
61            ty: PhantomData,
62            ts: PhantomData,
63        }
64    }
65
66    /// Create a Setter of this Signal
67    pub fn write_only(&self) -> WriteSignal<T> {
68        WriteSignal {
69            id: self.id,
70            ty: PhantomData,
71            ts: PhantomData,
72        }
73    }
74}
75
76impl<T: 'static> RwSignal<T> {
77    pub fn new(value: T) -> Self {
78        create_rw_signal(value)
79    }
80    pub fn new_split(value: T) -> (ReadSignal<T>, WriteSignal<T>) {
81        let sig = Self::new(value);
82        (sig.read_only(), sig.write_only())
83    }
84}
85
86/// Creates a new RwSignal which can act both as a setter and a getter.
87///
88/// Accessing the signal value in an Effect will make the Effect subscribe
89/// to the value change of the Signal. And whenever the signal value changes,
90/// it will trigger an effect run.
91pub fn create_rw_signal<T>(value: T) -> RwSignal<T>
92where
93    T: Any + 'static,
94{
95    let id = Signal::create(value);
96    id.set_scope();
97    RwSignal {
98        id,
99        ty: PhantomData,
100        ts: PhantomData,
101    }
102}
103
104/// A getter only Signal
105pub struct ReadSignal<T> {
106    pub(crate) id: Id,
107    pub(crate) ty: PhantomData<T>,
108    pub(crate) ts: PhantomData<NotThreadSafe>,
109}
110
111impl<T> Copy for ReadSignal<T> {}
112
113impl<T> Clone for ReadSignal<T> {
114    fn clone(&self) -> Self {
115        *self
116    }
117}
118
119impl<T> Eq for ReadSignal<T> {}
120
121impl<T> PartialEq for ReadSignal<T> {
122    fn eq(&self, other: &Self) -> bool {
123        self.id == other.id
124    }
125}
126
127/// A setter only Signal
128pub struct WriteSignal<T> {
129    pub(crate) id: Id,
130    pub(crate) ty: PhantomData<T>,
131    pub(crate) ts: PhantomData<NotThreadSafe>,
132}
133
134impl<T> Copy for WriteSignal<T> {}
135
136impl<T> Clone for WriteSignal<T> {
137    fn clone(&self) -> Self {
138        *self
139    }
140}
141
142impl<T> Eq for WriteSignal<T> {}
143
144impl<T> PartialEq for WriteSignal<T> {
145    fn eq(&self, other: &Self) -> bool {
146        self.id == other.id
147    }
148}
149
150/// Creates a new setter and getter Signal.
151///
152/// Accessing the signal value in an Effect will make the Effect subscribe
153/// to the value change of the Signal. And whenever the signal value changes,
154/// it will trigger an effect run.
155pub fn create_signal<T>(value: T) -> (ReadSignal<T>, WriteSignal<T>)
156where
157    T: Any + 'static,
158{
159    let s = create_rw_signal(value);
160    (s.read_only(), s.write_only())
161}
162
163/// The internal Signal where the value is stored, and effects are stored.
164#[derive(Clone)]
165pub(crate) struct Signal {
166    pub(crate) id: Id,
167    pub(crate) value: Rc<dyn Any>,
168    pub(crate) subscribers: Rc<RefCell<HashMap<Id, Rc<dyn EffectTrait>>>>,
169    pub(crate) ts: PhantomData<NotThreadSafe>,
170}
171
172impl Signal {
173    pub fn create<T>(value: T) -> Id
174    where
175        T: Any + 'static,
176    {
177        let id = Id::next();
178        let value = RefCell::new(value);
179        let signal = Signal {
180            id,
181            subscribers: Rc::new(RefCell::new(HashMap::new())),
182            value: Rc::new(value),
183            ts: PhantomData,
184        };
185        id.add_signal(signal);
186        id
187    }
188
189    pub fn borrow<T: 'static>(&self) -> Ref<'_, T> {
190        let value = self
191            .value
192            .downcast_ref::<RefCell<T>>()
193            .expect("to downcast signal type");
194        value.borrow()
195    }
196
197    pub(crate) fn get_untracked<T: Clone + 'static>(&self) -> T {
198        let value = self.borrow::<T>();
199        value.clone()
200    }
201
202    pub(crate) fn get<T: Clone + 'static>(&self) -> T {
203        self.subscribe();
204        self.get_untracked()
205    }
206
207    pub(crate) fn with_untracked<O, T: 'static>(&self, f: impl FnOnce(&T) -> O) -> O {
208        let value = self.borrow::<T>();
209        f(&value)
210    }
211
212    pub(crate) fn with<O, T: 'static>(&self, f: impl FnOnce(&T) -> O) -> O {
213        self.subscribe();
214        self.with_untracked(f)
215    }
216
217    pub(crate) fn update_value<U, T: 'static>(&self, f: impl FnOnce(&mut T) -> U) -> U {
218        let result = self
219            .value
220            .downcast_ref::<RefCell<T>>()
221            .expect("to downcast signal type");
222        let result = f(&mut result.borrow_mut());
223        self.run_effects();
224        result
225    }
226
227    pub(crate) fn subscribers(&self) -> HashMap<Id, Rc<dyn EffectTrait>> {
228        self.subscribers.borrow().clone()
229    }
230
231    pub(crate) fn run_effects(&self) {
232        // If we are batching then add it as a pending effect
233        if RUNTIME.with(|r| r.batching.get()) {
234            RUNTIME.with(|r| {
235                for (_, subscriber) in self.subscribers() {
236                    r.add_pending_effect(subscriber);
237                }
238            });
239            return;
240        }
241
242        for (_, subscriber) in self.subscribers() {
243            run_effect(subscriber);
244        }
245    }
246
247    pub(crate) fn subscribe(&self) {
248        RUNTIME.with(|runtime| {
249            if let Some(effect) = runtime.current_effect.borrow().as_ref() {
250                self.subscribers
251                    .borrow_mut()
252                    .insert(effect.id(), effect.clone());
253                effect.add_observer(self.id);
254            }
255        });
256    }
257}
258
259impl<T: Clone> SignalGet<T> for RwSignal<T> {
260    fn id(&self) -> Id {
261        self.id
262    }
263}
264
265impl<T> SignalWith<T> for RwSignal<T> {
266    fn id(&self) -> Id {
267        self.id
268    }
269}
270
271impl<T> SignalTrack<T> for RwSignal<T> {
272    fn id(&self) -> Id {
273        self.id
274    }
275}
276
277impl<T> SignalRead<T> for RwSignal<T> {
278    fn id(&self) -> Id {
279        self.id
280    }
281}
282
283impl<T> SignalUpdate<T> for RwSignal<T> {
284    fn id(&self) -> Id {
285        self.id
286    }
287}
288
289impl<T> SignalWrite<T> for RwSignal<T> {
290    fn id(&self) -> Id {
291        self.id
292    }
293}
294
295impl<T: Clone> SignalGet<T> for ReadSignal<T> {
296    fn id(&self) -> Id {
297        self.id
298    }
299}
300
301impl<T> SignalWith<T> for ReadSignal<T> {
302    fn id(&self) -> Id {
303        self.id
304    }
305}
306
307impl<T> SignalTrack<T> for ReadSignal<T> {
308    fn id(&self) -> Id {
309        self.id
310    }
311}
312
313impl<T> SignalRead<T> for ReadSignal<T> {
314    fn id(&self) -> Id {
315        self.id
316    }
317}
318
319impl<T> SignalUpdate<T> for WriteSignal<T> {
320    fn id(&self) -> Id {
321        self.id
322    }
323}
324
325impl<T> SignalWrite<T> for WriteSignal<T> {
326    fn id(&self) -> Id {
327        self.id
328    }
329}