floem_reactive/
read.rs

1use std::{
2    cell::{Ref, RefCell},
3    marker::PhantomData,
4    rc::Rc,
5};
6
7use crate::{id::Id, signal::NotThreadSafe};
8
9#[derive(Clone)]
10pub struct ReadSignalValue<T> {
11    pub(crate) value: Rc<RefCell<T>>,
12    pub(crate) ts: PhantomData<NotThreadSafe>,
13}
14
15impl<T> ReadSignalValue<T> {
16    /// Borrows the current value stored in the Signal
17    pub fn borrow(&self) -> Ref<'_, T> {
18        self.value.borrow()
19    }
20}
21
22pub trait SignalGet<T: Clone> {
23    /// get the Signal Id
24    fn id(&self) -> Id;
25
26    /// Clones and returns the current value stored in the Signal, but it doesn't subscribe
27    /// to the current running effect.
28    fn get_untracked(&self) -> T
29    where
30        T: 'static,
31    {
32        self.try_get_untracked().unwrap()
33    }
34
35    /// Clones and returns the current value stored in the Signal, and subscribes
36    /// to the current running effect to this Signal.
37    fn get(&self) -> T
38    where
39        T: 'static,
40    {
41        self.try_get().unwrap()
42    }
43
44    /// Try to clone and return the current value stored in the Signal, and returns None
45    /// if it's already disposed. It subscribes to the current running effect.
46    fn try_get(&self) -> Option<T>
47    where
48        T: 'static,
49    {
50        self.id().signal().map(|signal| signal.get())
51    }
52
53    /// Try to clone and return the current value stored in the Signal, and returns None
54    /// if it's already disposed. It doesn't subscribe to the current running effect.
55    fn try_get_untracked(&self) -> Option<T>
56    where
57        T: 'static,
58    {
59        self.id().signal().map(|signal| signal.get_untracked())
60    }
61}
62
63pub trait SignalTrack<T> {
64    fn id(&self) -> Id;
65    /// Only subscribes to the current running effect to this Signal.
66    ///
67    fn track(&self) {
68        self.id().signal().unwrap().subscribe();
69    }
70
71    /// If the signal isn't disposed,
72    // subscribes to the current running effect to this Signal.
73    fn try_track(&self) {
74        if let Some(signal) = self.id().signal() {
75            signal.subscribe();
76        }
77    }
78}
79
80pub trait SignalWith<T> {
81    /// get the Signal Id
82    fn id(&self) -> Id;
83
84    /// Applies a closure to the current value stored in the Signal, and subscribes
85    /// to the current running effect to this Memo.
86    fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O
87    where
88        T: 'static,
89    {
90        self.id().signal().unwrap().with(f)
91    }
92
93    /// Applies a closure to the current value stored in the Signal, but it doesn't subscribe
94    /// to the current running effect.
95    fn with_untracked<O>(&self, f: impl FnOnce(&T) -> O) -> O
96    where
97        T: 'static,
98    {
99        self.id().signal().unwrap().with_untracked(f)
100    }
101
102    /// If the signal isn't disposed, applies a closure to the current value stored in the Signal.
103    /// It subscribes to the current running effect.
104    fn try_with<O>(&self, f: impl FnOnce(Option<&T>) -> O) -> O
105    where
106        T: 'static,
107    {
108        if let Some(signal) = self.id().signal() {
109            signal.with(|v| f(Some(v)))
110        } else {
111            f(None)
112        }
113    }
114
115    /// If the signal isn't disposed, applies a closure to the current value stored in the Signal,
116    /// but it doesn't subscribe to the current running effect.
117    fn try_with_untracked<O>(&self, f: impl FnOnce(Option<&T>) -> O) -> O
118    where
119        T: 'static,
120    {
121        if let Some(signal) = self.id().signal() {
122            signal.with_untracked(|v| f(Some(v)))
123        } else {
124            f(None)
125        }
126    }
127}
128
129pub trait SignalRead<T> {
130    /// get the Signal Id
131    fn id(&self) -> Id;
132
133    /// Reads the data stored in the Signal to a RefCell, so that you can `borrow()`
134    /// and access the data.
135    /// It subscribes to the current running effect.
136    fn read(&self) -> ReadSignalValue<T>
137    where
138        T: 'static,
139    {
140        self.try_read().unwrap()
141    }
142
143    /// Reads the data stored in the Signal to a RefCell, so that you can `borrow()`
144    /// and access the data.
145    /// It doesn't subscribe to the current running effect.
146    fn read_untracked(&self) -> ReadSignalValue<T>
147    where
148        T: 'static,
149    {
150        self.try_read_untracked().unwrap()
151    }
152
153    /// If the signal isn't disposed,
154    /// reads the data stored in the Signal to a RefCell, so that you can `borrow()`
155    /// and access the data.
156    /// It subscribes to the current running effect.
157    fn try_read(&self) -> Option<ReadSignalValue<T>>
158    where
159        T: 'static,
160    {
161        if let Some(signal) = self.id().signal() {
162            signal.subscribe();
163            Some(ReadSignalValue {
164                value: signal
165                    .value
166                    .clone()
167                    .downcast::<RefCell<T>>()
168                    .expect("to downcast signal type"),
169                ts: PhantomData,
170            })
171        } else {
172            None
173        }
174    }
175
176    /// If the signal isn't disposed,
177    /// reads the data stored in the Signal to a RefCell, so that you can `borrow()`
178    /// and access the data.
179    /// It doesn't subscribe to the current running effect.
180    fn try_read_untracked(&self) -> Option<ReadSignalValue<T>>
181    where
182        T: 'static,
183    {
184        if let Some(signal) = self.id().signal() {
185            Some(ReadSignalValue {
186                value: signal
187                    .value
188                    .clone()
189                    .downcast::<RefCell<T>>()
190                    .expect("to downcast signal type"),
191                ts: PhantomData,
192            })
193        } else {
194            None
195        }
196    }
197}