1use std::any::Any;
2use std::cell::{Cell, RefCell};
3use std::rc::{Rc, Weak};
4
5use crate::effects::Dispose;
6
7thread_local! {
8 static CURRENT_SCOPE: RefCell<Option<Weak<ScopeInner>>> = const { RefCell::new(None) };
9}
10
11pub struct Scope {
12 inner: Rc<ScopeInner>,
13}
14
15struct ScopeInner {
16 disposers: RefCell<Vec<Box<dyn FnOnce()>>>,
17 children: RefCell<Vec<Scope>>,
18 memo_cache: RefCell<std::collections::HashMap<String, Box<dyn Any>>>,
19 disposed: Cell<bool>,
20}
21
22impl Default for Scope {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28impl Scope {
29 pub fn new() -> Self {
30 Self {
31 inner: Rc::new(ScopeInner {
32 disposers: RefCell::new(Vec::new()),
33 children: RefCell::new(Vec::new()),
34 memo_cache: RefCell::new(std::collections::HashMap::new()),
35 disposed: Cell::new(false),
36 }),
37 }
38 }
39
40 pub fn run<R>(&self, f: impl FnOnce() -> R) -> R {
41 CURRENT_SCOPE.with(|current| {
42 let prev = current.borrow().clone();
43 *current.borrow_mut() = Some(Rc::downgrade(&self.inner));
44 let result = f();
45 *current.borrow_mut() = prev;
46 result
47 })
48 }
49
50 pub fn add_disposer(&self, disposer: impl FnOnce() + 'static) {
51 self.inner.disposers.borrow_mut().push(Box::new(disposer));
52 }
53
54 pub fn child(&self) -> Scope {
55 let child = Scope::new();
56 self.inner.children.borrow_mut().push(child.clone());
57 child
58 }
59
60 pub fn dispose(self) {
61 if self.inner.disposed.replace(true) {
62 return; }
64 let children = std::mem::take(&mut *self.inner.children.borrow_mut());
66 for child in children {
67 child.dispose();
68 }
69
70 let disposers = std::mem::take(&mut *self.inner.disposers.borrow_mut());
72 for disposer in disposers {
73 disposer();
74 }
75 }
76}
77
78impl Clone for Scope {
79 fn clone(&self) -> Self {
80 Self {
81 inner: self.inner.clone(),
82 }
83 }
84}
85
86pub fn current_scope() -> Option<Scope> {
87 CURRENT_SCOPE.with(|current| {
88 current
89 .borrow()
90 .as_ref()
91 .and_then(|weak| weak.upgrade().map(|inner| Scope { inner }))
92 })
93}
94
95pub fn scoped_effect<F>(f: F)
100where
101 F: FnOnce() -> Dispose + 'static,
102{
103 if let Some(scope) = current_scope() {
104 let cleanup = f();
105 scope.add_disposer(move || cleanup.run());
106 } else {
107 let _cleanup = f();
109 }
110}
111
112impl Drop for ScopeInner {
113 fn drop(&mut self) {
114 if self.disposed.replace(true) {
115 return; }
117 let children = std::mem::take(&mut *self.children.borrow_mut());
118 for child in children {
119 drop(child);
120 }
121
122 let disposers = std::mem::take(&mut *self.disposers.borrow_mut());
123 for disposer in disposers {
124 disposer();
125 }
126 }
127}