1use std::{any::Any, cell::RefCell, collections::HashSet, marker::PhantomData, mem, rc::Rc};
2
3use crate::{
4 id::Id,
5 runtime::RUNTIME,
6 scope::{with_scope, Scope},
7 signal::NotThreadSafe,
8};
9
10pub(crate) trait EffectTrait {
11 fn id(&self) -> Id;
12 fn run(&self) -> bool;
13 fn add_observer(&self, id: Id);
14 fn clear_observers(&self) -> HashSet<Id>;
15}
16
17struct Effect<T, F>
18where
19 T: 'static,
20 F: Fn(Option<T>) -> T,
21{
22 id: Id,
23 f: F,
24 value: RefCell<Option<T>>,
25 observers: RefCell<HashSet<Id>>,
26 ts: PhantomData<NotThreadSafe>,
27}
28
29impl<T, F> Drop for Effect<T, F>
30where
31 T: 'static,
32 F: Fn(Option<T>) -> T,
33{
34 fn drop(&mut self) {
35 self.id.dispose();
36 }
37}
38
39pub fn create_effect<T>(f: impl Fn(Option<T>) -> T + 'static)
47where
48 T: Any + 'static,
49{
50 let id = Id::next();
51 let effect = Rc::new(Effect {
52 id,
53 f,
54 value: RefCell::new(None),
55 observers: RefCell::new(HashSet::default()),
56 ts: PhantomData,
57 });
58 id.set_scope();
59
60 run_initial_effect(effect);
61}
62
63struct UpdaterEffect<T, I, C, U>
64where
65 C: Fn(Option<T>) -> (I, T),
66 U: Fn(I, T) -> T,
67{
68 id: Id,
69 compute: C,
70 on_change: U,
71 value: RefCell<Option<T>>,
72 observers: RefCell<HashSet<Id>>,
73}
74
75impl<T, I, C, U> Drop for UpdaterEffect<T, I, C, U>
76where
77 C: Fn(Option<T>) -> (I, T),
78 U: Fn(I, T) -> T,
79{
80 fn drop(&mut self) {
81 self.id.dispose();
82 }
83}
84
85pub fn create_updater<R>(compute: impl Fn() -> R + 'static, on_change: impl Fn(R) + 'static) -> R
88where
89 R: 'static,
90{
91 create_stateful_updater(move |_| (compute(), ()), move |r, _| on_change(r))
92}
93
94pub fn create_stateful_updater<T, R>(
97 compute: impl Fn(Option<T>) -> (R, T) + 'static,
98 on_change: impl Fn(R, T) -> T + 'static,
99) -> R
100where
101 T: Any + 'static,
102 R: 'static,
103{
104 let id = Id::next();
105 let effect = Rc::new(UpdaterEffect {
106 id,
107 compute,
108 on_change,
109 value: RefCell::new(None),
110 observers: RefCell::new(HashSet::default()),
111 });
112 id.set_scope();
113
114 run_initial_updater_effect(effect)
115}
116
117pub fn untrack<T>(f: impl FnOnce() -> T) -> T {
119 let prev_effect = RUNTIME.with(|runtime| runtime.current_effect.borrow_mut().take());
120 let result = f();
121 RUNTIME.with(|runtime| {
122 *runtime.current_effect.borrow_mut() = prev_effect;
123 });
124 result
125}
126
127pub fn batch<T>(f: impl FnOnce() -> T) -> T {
128 let already_batching = RUNTIME.with(|runtime| {
129 let batching = runtime.batching.get();
130 if !batching {
131 runtime.batching.set(true);
132 }
133
134 batching
135 });
136
137 let result = f();
138 if !already_batching {
139 RUNTIME.with(|runtime| {
140 runtime.batching.set(false);
141 runtime.run_pending_effects();
142 });
143 }
144
145 result
146}
147
148pub(crate) fn run_initial_effect(effect: Rc<dyn EffectTrait>) {
149 let effect_id = effect.id();
150
151 RUNTIME.with(|runtime| {
152 *runtime.current_effect.borrow_mut() = Some(effect.clone());
153
154 let effect_scope = Scope(effect_id, PhantomData);
155 with_scope(effect_scope, || {
156 effect_scope.track();
157 effect.run();
158 });
159
160 *runtime.current_effect.borrow_mut() = None;
161 });
162}
163
164pub(crate) fn run_effect(effect: Rc<dyn EffectTrait>) {
165 let effect_id = effect.id();
166 effect_id.dispose();
167
168 observer_clean_up(&effect);
169
170 RUNTIME.with(|runtime| {
171 *runtime.current_effect.borrow_mut() = Some(effect.clone());
172
173 let effect_scope = Scope(effect_id, PhantomData);
174 with_scope(effect_scope, move || {
175 effect_scope.track();
176 effect.run();
177 });
178
179 *runtime.current_effect.borrow_mut() = None;
180 });
181}
182
183fn run_initial_updater_effect<T, I, C, U>(effect: Rc<UpdaterEffect<T, I, C, U>>) -> I
184where
185 T: 'static,
186 I: 'static,
187 C: Fn(Option<T>) -> (I, T) + 'static,
188 U: Fn(I, T) -> T + 'static,
189{
190 let effect_id = effect.id();
191
192 let result = RUNTIME.with(|runtime| {
193 *runtime.current_effect.borrow_mut() = Some(effect.clone());
194
195 let effect_scope = Scope(effect_id, PhantomData);
196 let (result, new_value) = with_scope(effect_scope, || {
197 effect_scope.track();
198 (effect.compute)(None)
199 });
200
201 *effect.value.borrow_mut() = Some(new_value);
203
204 *runtime.current_effect.borrow_mut() = None;
205
206 result
207 });
208
209 result
210}
211
212pub(crate) fn observer_clean_up(effect: &Rc<dyn EffectTrait>) {
216 let effect_id = effect.id();
217 let observers = effect.clear_observers();
218 for observer in observers {
219 if let Some(signal) = observer.signal() {
220 signal.subscribers.borrow_mut().remove(&effect_id);
221 }
222 }
223}
224
225impl<T, F> EffectTrait for Effect<T, F>
226where
227 T: 'static,
228 F: Fn(Option<T>) -> T,
229{
230 fn id(&self) -> Id {
231 self.id
232 }
233
234 fn run(&self) -> bool {
235 let curr_value = self.value.borrow_mut().take();
236
237 let new_value = (self.f)(curr_value);
239
240 *self.value.borrow_mut() = Some(new_value);
241
242 true
243 }
244
245 fn add_observer(&self, id: Id) {
246 self.observers.borrow_mut().insert(id);
247 }
248
249 fn clear_observers(&self) -> HashSet<Id> {
250 mem::take(&mut *self.observers.borrow_mut())
251 }
252}
253
254impl<T, I, C, U> EffectTrait for UpdaterEffect<T, I, C, U>
255where
256 T: 'static,
257 C: Fn(Option<T>) -> (I, T),
258 U: Fn(I, T) -> T,
259{
260 fn id(&self) -> Id {
261 self.id
262 }
263
264 fn run(&self) -> bool {
265 let curr_value = self.value.borrow_mut().take();
266
267 let (i, t) = (self.compute)(curr_value);
269 let new_value = (self.on_change)(i, t);
270
271 *self.value.borrow_mut() = Some(new_value);
272 true
273 }
274
275 fn add_observer(&self, id: Id) {
276 self.observers.borrow_mut().insert(id);
277 }
278
279 fn clear_observers(&self) -> HashSet<Id> {
280 mem::take(&mut *self.observers.borrow_mut())
281 }
282}
283
284pub struct SignalTracker {
285 id: Id,
286 on_change: Rc<dyn Fn()>,
287}
288
289impl Drop for SignalTracker {
290 fn drop(&mut self) {
291 self.id.dispose();
292 }
293}
294
295pub fn create_tracker(on_change: impl Fn() + 'static) -> SignalTracker {
296 let id = Id::next();
297
298 SignalTracker {
299 id,
300 on_change: Rc::new(on_change),
301 }
302}
303
304impl SignalTracker {
305 pub fn track<T: 'static>(&self, f: impl FnOnce() -> T) -> T {
306 self.id.dispose();
308
309 let prev_effect = RUNTIME.with(|runtime| runtime.current_effect.borrow_mut().take());
310
311 let tracking_effect = Rc::new(TrackingEffect {
312 id: self.id,
313 observers: RefCell::new(HashSet::default()),
314 on_change: self.on_change.clone(),
315 });
316
317 RUNTIME.with(|runtime| {
318 *runtime.current_effect.borrow_mut() = Some(tracking_effect.clone());
319 });
320
321 let effect_scope = Scope(self.id, PhantomData);
322 let result = with_scope(effect_scope, || {
323 effect_scope.track();
324 f()
325 });
326
327 RUNTIME.with(|runtime| {
328 *runtime.current_effect.borrow_mut() = prev_effect;
329 });
330
331 result
332 }
333}
334
335struct TrackingEffect {
336 id: Id,
337 observers: RefCell<HashSet<Id>>,
338 on_change: Rc<dyn Fn()>,
339}
340
341impl EffectTrait for TrackingEffect {
342 fn id(&self) -> Id {
343 self.id
344 }
345
346 fn run(&self) -> bool {
347 (self.on_change)();
348 true
349 }
350
351 fn add_observer(&self, id: Id) {
352 self.observers.borrow_mut().insert(id);
353 }
354
355 fn clear_observers(&self) -> HashSet<Id> {
356 mem::take(&mut *self.observers.borrow_mut())
357 }
358}