repose_core/
scope.rs

1use std::any::Any;
2use std::cell::RefCell;
3use std::rc::{Rc, Weak};
4
5thread_local! {
6    static CURRENT_SCOPE: RefCell<Option<Weak<ScopeInner>>> = RefCell::new(None);
7}
8
9pub struct Scope {
10    inner: Rc<ScopeInner>,
11}
12
13struct ScopeInner {
14    disposers: RefCell<Vec<Box<dyn FnOnce()>>>,
15    children: RefCell<Vec<Scope>>,
16    memo_cache: RefCell<std::collections::HashMap<String, Box<dyn Any>>>,
17}
18
19impl Scope {
20    pub fn new() -> Self {
21        Self {
22            inner: Rc::new(ScopeInner {
23                disposers: RefCell::new(Vec::new()),
24                children: RefCell::new(Vec::new()),
25                memo_cache: RefCell::new(std::collections::HashMap::new()),
26            }),
27        }
28    }
29
30    pub fn run<R>(&self, f: impl FnOnce() -> R) -> R {
31        CURRENT_SCOPE.with(|current| {
32            let prev = current.borrow().clone();
33            *current.borrow_mut() = Some(Rc::downgrade(&self.inner));
34            let result = f();
35            *current.borrow_mut() = prev;
36            result
37        })
38    }
39
40    pub fn add_disposer(&self, disposer: impl FnOnce() + 'static) {
41        self.inner.disposers.borrow_mut().push(Box::new(disposer));
42    }
43
44    pub fn child(&self) -> Scope {
45        let child = Scope::new();
46        self.inner.children.borrow_mut().push(child.clone());
47        child
48    }
49
50    pub fn dispose(self) {
51        // Dispose children first
52        let children = std::mem::take(&mut *self.inner.children.borrow_mut());
53        for child in children {
54            child.dispose();
55        }
56
57        // Run disposers
58        let disposers = std::mem::take(&mut *self.inner.disposers.borrow_mut());
59        for disposer in disposers {
60            disposer();
61        }
62    }
63}
64
65impl Clone for Scope {
66    fn clone(&self) -> Self {
67        Self {
68            inner: self.inner.clone(),
69        }
70    }
71}
72
73pub fn current_scope() -> Option<Scope> {
74    CURRENT_SCOPE.with(|current| {
75        current
76            .borrow()
77            .as_ref()
78            .and_then(|weak| weak.upgrade().map(|inner| Scope { inner }))
79    })
80}
81
82/// Scoped effect that auto-cleans up
83pub fn scoped_effect<F>(f: F)
84where
85    F: FnOnce() -> Box<dyn FnOnce()> + 'static,
86{
87    if let Some(scope) = current_scope() {
88        let cleanup = f();
89        scope.add_disposer(cleanup);
90    } else {
91        // No scope, run immediately and leak (old behavior)
92        let _ = f();
93    }
94}
95
96impl Drop for ScopeInner {
97    fn drop(&mut self) {
98        let children = std::mem::take(&mut *self.children.borrow_mut());
99        for child in children {
100            drop(child);
101        }
102
103        let disposers = std::mem::take(&mut *self.disposers.borrow_mut());
104        for disposer in disposers {
105            disposer();
106        }
107    }
108}