1use crate::{Effect, effect, untrack};
3use std::{
4 any::Any,
5 cell::{Cell, RefCell},
6 rc::Rc,
7};
8
9#[must_use = "retain the guard for the lifetime of the external work"]
12pub struct Cleanup(Option<Box<dyn FnOnce()>>);
13
14impl Cleanup {
15 pub fn new(callback: impl FnOnce() + 'static) -> Self {
16 Self(Some(Box::new(callback)))
17 }
18}
19
20impl Drop for Cleanup {
21 fn drop(&mut self) {
22 if let Some(callback) = self.0.take() {
23 untrack(callback);
24 }
25 }
26}
27
28#[derive(Default)]
29struct GuardSlot {
30 stopped: Cell<bool>,
31 value: RefCell<Option<Box<dyn Any>>>,
32}
33
34impl GuardSlot {
35 fn clear(&self) {
36 let value = self.value.take();
37 untrack(|| drop(value));
38 }
39}
40
41#[must_use = "retain the effect while its external work should remain active"]
44pub struct CleanupEffect {
45 effect: Effect,
46 slot: Rc<GuardSlot>,
47}
48
49impl CleanupEffect {
50 pub fn dispose(&self) {
51 self.slot.stopped.set(true);
52 self.effect.dispose();
53 self.slot.clear();
54 }
55}
56
57impl Drop for CleanupEffect {
58 fn drop(&mut self) {
59 self.dispose();
60 }
61}
62
63pub fn effect_with_cleanup<G: 'static>(mut setup: impl FnMut() -> G + 'static) -> CleanupEffect {
68 let slot = Rc::new(GuardSlot::default());
69 let captured = slot.clone();
70 let effect = effect(move || {
71 captured.clear();
72 if captured.stopped.get() {
73 return;
74 }
75 let guard = setup();
76 if captured.stopped.get() {
77 untrack(|| drop(guard));
78 } else {
79 *captured.value.borrow_mut() = Some(Box::new(guard));
80 }
81 });
82 CleanupEffect { effect, slot }
83}