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 memo<T: 'static>(&self, key: &str, init: impl FnOnce() -> T) -> Rc<T> {
58 let mut cache = self.inner.memo_cache.borrow_mut();
59 if let Some(existing) = cache.get(key) {
60 if let Some(v) = existing.downcast_ref::<Rc<T>>() {
61 return v.clone();
62 }
63 }
64 let val: Rc<T> = Rc::new(init());
65 cache.insert(key.to_string(), Box::new(val.clone()));
66 val
67 }
68
69 pub fn child(&self) -> Scope {
70 let child = Scope::new();
71 self.inner.children.borrow_mut().push(child.clone());
72 child
73 }
74
75 pub fn dispose(self) {
76 if self.inner.disposed.replace(true) {
77 return; }
79 let children = std::mem::take(&mut *self.inner.children.borrow_mut());
81 for child in children {
82 child.dispose();
83 }
84
85 let disposers = std::mem::take(&mut *self.inner.disposers.borrow_mut());
87 for disposer in disposers {
88 disposer();
89 }
90 }
91}
92
93impl Clone for Scope {
94 fn clone(&self) -> Self {
95 Self {
96 inner: self.inner.clone(),
97 }
98 }
99}
100
101pub fn current_scope() -> Option<Scope> {
102 CURRENT_SCOPE.with(|current| {
103 current
104 .borrow()
105 .as_ref()
106 .and_then(|weak| weak.upgrade().map(|inner| Scope { inner }))
107 })
108}
109
110pub fn scope_memo<T: 'static>(key: &str, init: impl FnOnce() -> T) -> Rc<T> {
117 match current_scope() {
118 Some(scope) => scope.memo(key, init),
119 None => Rc::new(init()),
120 }
121}
122
123pub fn scoped_effect<F>(f: F)
128where
129 F: FnOnce() -> Dispose + 'static,
130{
131 if let Some(scope) = current_scope() {
132 let cleanup = f();
133 scope.add_disposer(move || cleanup.run());
134 } else {
135 let _cleanup = f();
137 }
138}
139
140impl Drop for ScopeInner {
141 fn drop(&mut self) {
142 if self.disposed.replace(true) {
143 return; }
145 let children = std::mem::take(&mut *self.children.borrow_mut());
146 for child in children {
147 drop(child);
148 }
149
150 let disposers = std::mem::take(&mut *self.disposers.borrow_mut());
151 for disposer in disposers {
152 disposer();
153 }
154 }
155}