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}