1use std::any::Any;
2use std::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}
20
21impl Default for Scope {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl Scope {
28 pub fn new() -> Self {
29 Self {
30 inner: Rc::new(ScopeInner {
31 disposers: RefCell::new(Vec::new()),
32 children: RefCell::new(Vec::new()),
33 memo_cache: RefCell::new(std::collections::HashMap::new()),
34 }),
35 }
36 }
37
38 pub fn run<R>(&self, f: impl FnOnce() -> R) -> R {
39 CURRENT_SCOPE.with(|current| {
40 let prev = current.borrow().clone();
41 *current.borrow_mut() = Some(Rc::downgrade(&self.inner));
42 let result = f();
43 *current.borrow_mut() = prev;
44 result
45 })
46 }
47
48 pub fn add_disposer(&self, disposer: impl FnOnce() + 'static) {
49 self.inner.disposers.borrow_mut().push(Box::new(disposer));
50 }
51
52 pub fn child(&self) -> Scope {
53 let child = Scope::new();
54 self.inner.children.borrow_mut().push(child.clone());
55 child
56 }
57
58 pub fn dispose(self) {
59 let children = std::mem::take(&mut *self.inner.children.borrow_mut());
61 for child in children {
62 child.dispose();
63 }
64
65 let disposers = std::mem::take(&mut *self.inner.disposers.borrow_mut());
67 for disposer in disposers {
68 disposer();
69 }
70 }
71}
72
73impl Clone for Scope {
74 fn clone(&self) -> Self {
75 Self {
76 inner: self.inner.clone(),
77 }
78 }
79}
80
81pub fn current_scope() -> Option<Scope> {
82 CURRENT_SCOPE.with(|current| {
83 current
84 .borrow()
85 .as_ref()
86 .and_then(|weak| weak.upgrade().map(|inner| Scope { inner }))
87 })
88}
89
90pub fn scoped_effect<F>(f: F)
95where
96 F: FnOnce() -> Dispose + 'static,
97{
98 if let Some(scope) = current_scope() {
99 let cleanup = f();
100 scope.add_disposer(move || cleanup.run());
101 } else {
102 let _cleanup = f();
104 }
105}
106
107impl Drop for ScopeInner {
108 fn drop(&mut self) {
109 let children = std::mem::take(&mut *self.children.borrow_mut());
110 for child in children {
111 drop(child);
112 }
113
114 let disposers = std::mem::take(&mut *self.disposers.borrow_mut());
115 for disposer in disposers {
116 disposer();
117 }
118 }
119}