1use std::{any::Any, cell::RefCell, collections::HashMap, fmt, marker::PhantomData, rc::Rc};
2
3use crate::{
4 create_effect, create_updater,
5 id::Id,
6 memo::{create_memo, Memo},
7 runtime::RUNTIME,
8 signal::{
9 create_rw_signal, create_signal, NotThreadSafe, ReadSignal, RwSignal, Signal, WriteSignal,
10 },
11 trigger::{create_trigger, Trigger},
12};
13
14#[derive(Clone, Copy)]
20pub struct Scope(pub(crate) Id, pub(crate) PhantomData<NotThreadSafe>);
21
22impl Default for Scope {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28impl fmt::Debug for Scope {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 let mut s = f.debug_struct("Scope");
31 s.field("id", &self.0);
32 s.finish()
33 }
34}
35
36impl Scope {
37 pub fn new() -> Self {
39 Self(Id::next(), PhantomData)
40 }
41
42 pub fn current() -> Scope {
45 RUNTIME.with(|runtime| Scope(*runtime.current_scope.borrow(), PhantomData))
46 }
47
48 pub fn create_child(&self) -> Scope {
50 let child = Id::next();
51 RUNTIME.with(|runtime| {
52 let mut children = runtime.children.borrow_mut();
53 let children = children.entry(self.0).or_default();
54 children.insert(child);
55 });
56 Scope(child, PhantomData)
57 }
58
59 pub fn create_signal<T>(self, value: T) -> (ReadSignal<T>, WriteSignal<T>)
61 where
62 T: Any + 'static,
63 {
64 with_scope(self, || create_signal(value))
65 }
66
67 pub fn create_rw_signal<T>(self, value: T) -> RwSignal<T>
69 where
70 T: Any + 'static,
71 {
72 with_scope(self, || create_rw_signal(value))
73 }
74
75 pub fn create_memo<T>(self, f: impl Fn(Option<&T>) -> T + 'static) -> Memo<T>
77 where
78 T: PartialEq + 'static,
79 {
80 with_scope(self, || create_memo(f))
81 }
82
83 pub fn create_trigger(self) -> Trigger {
85 with_scope(self, create_trigger)
86 }
87
88 pub fn create_effect<T>(self, f: impl Fn(Option<T>) -> T + 'static)
90 where
91 T: Any + 'static,
92 {
93 with_scope(self, || create_effect(f))
94 }
95
96 pub fn create_updater<R>(
98 self,
99 compute: impl Fn() -> R + 'static,
100 on_change: impl Fn(R) + 'static,
101 ) -> R
102 where
103 R: 'static,
104 {
105 with_scope(self, || create_updater(compute, on_change))
106 }
107
108 pub fn track(&self) {
111 let tracker = if let Some(signal) = self.0.signal() {
112 signal
113 } else {
114 let signal = Signal {
115 id: self.0,
116 subscribers: Rc::new(RefCell::new(HashMap::new())),
117 value: Rc::new(RefCell::new(())),
118 ts: PhantomData,
119 };
120 self.0.add_signal(signal.clone());
121 signal
122 };
123 tracker.subscribe();
124 }
125
126 pub fn dispose(&self) {
129 self.0.dispose();
130 }
131}
132
133pub fn with_scope<T>(scope: Scope, f: impl FnOnce() -> T) -> T
135where
136 T: 'static,
137{
138 let prev_scope = RUNTIME.with(|runtime| {
139 let mut current_scope = runtime.current_scope.borrow_mut();
140 let prev_scope = *current_scope;
141 *current_scope = scope.0;
142 prev_scope
143 });
144
145 let result = f();
146
147 RUNTIME.with(|runtime| {
148 *runtime.current_scope.borrow_mut() = prev_scope;
149 });
150
151 result
152}
153
154pub fn as_child_of_current_scope<T, U>(f: impl Fn(T) -> U + 'static) -> impl Fn(T) -> (U, Scope)
157where
158 T: 'static,
159{
160 let current_scope = Scope::current();
161 move |t| {
162 let scope = current_scope.create_child();
163 let prev_scope = RUNTIME.with(|runtime| {
164 let mut current_scope = runtime.current_scope.borrow_mut();
165 let prev_scope = *current_scope;
166 *current_scope = scope.0;
167 prev_scope
168 });
169
170 let result = f(t);
171
172 RUNTIME.with(|runtime| {
173 *runtime.current_scope.borrow_mut() = prev_scope;
174 });
175
176 (result, scope)
177 }
178}