1use std::cell::RefCell;
2use std::collections::{HashMap, HashSet};
3use std::sync::Arc;
4
5use crate::Node;
6
7thread_local! {
8 static SCOPE_STACK: RefCell<Vec<usize>> = RefCell::new(Vec::new());
10
11 static CURRENT_SCOPE: RefCell<Option<usize>> = RefCell::new(None);
13
14 static RENDERING_SCOPE: RefCell<bool> = RefCell::new(false);
16
17 static NEXT_SCOPE_ID: RefCell<usize> = RefCell::new(1);
19
20 static SCOPE_SIGNAL_COUNTERS: RefCell<HashMap<usize, usize>> = RefCell::new(HashMap::new());
22
23 static SCOPE_SIGNAL_CHANGES: RefCell<HashSet<(usize, usize)>> = RefCell::new(HashSet::new());
25
26 static SIGNALS: RefCell<HashMap<(usize, usize), SignalValue>> = RefCell::new(HashMap::new());
27
28 static SCOPE_FUNCTIONS: RefCell<HashMap<usize, Arc<dyn Fn() -> Node + Send>>> = RefCell::new(HashMap::new());
30
31 static SCOPE_EFFECT_COUNTERS: RefCell<HashMap<usize, usize>> = RefCell::new(HashMap::new());
33
34 static SCOPE_EFFECTS: RefCell<HashMap<(usize, usize), Box<dyn Fn() + Send + Sync>>> = RefCell::new(HashMap::new());
36
37 static SIGNAL_DEPENDENCIES: RefCell<HashMap<(usize, usize), HashSet<usize>>> = RefCell::new(HashMap::new());
39
40 static PENDING_SCOPE_RENDERS: RefCell<HashSet<usize>> = RefCell::new(HashSet::new());
42}
43
44pub trait DynamicValue {
45 fn as_any(&self) -> Option<&dyn std::any::Any>;
46}
47
48impl DynamicValue for String {
49 fn as_any(&self) -> Option<&dyn std::any::Any> {
50 Some(self)
51 }
52}
53
54impl DynamicValue for &'static str {
55 fn as_any(&self) -> Option<&dyn std::any::Any> {
56 Some(self)
57 }
58}
59
60impl DynamicValue for i64 {
61 fn as_any(&self) -> Option<&dyn std::any::Any> {
62 Some(self)
63 }
64}
65
66impl DynamicValue for i128 {
67 fn as_any(&self) -> Option<&dyn std::any::Any> {
68 Some(self)
69 }
70}
71
72impl DynamicValue for i16 {
73 fn as_any(&self) -> Option<&dyn std::any::Any> {
74 Some(self)
75 }
76}
77
78impl DynamicValue for i32 {
79 fn as_any(&self) -> Option<&dyn std::any::Any> {
80 Some(self)
81 }
82}
83
84impl DynamicValue for i8 {
85 fn as_any(&self) -> Option<&dyn std::any::Any> {
86 Some(self)
87 }
88}
89
90impl DynamicValue for usize {
91 fn as_any(&self) -> Option<&dyn std::any::Any> {
92 Some(self)
93 }
94}
95
96impl DynamicValue for u64 {
97 fn as_any(&self) -> Option<&dyn std::any::Any> {
98 Some(self)
99 }
100}
101
102impl DynamicValue for u128 {
103 fn as_any(&self) -> Option<&dyn std::any::Any> {
104 Some(self)
105 }
106}
107
108impl DynamicValue for u16 {
109 fn as_any(&self) -> Option<&dyn std::any::Any> {
110 Some(self)
111 }
112}
113
114impl DynamicValue for u32 {
115 fn as_any(&self) -> Option<&dyn std::any::Any> {
116 Some(self)
117 }
118}
119
120impl DynamicValue for u8 {
121 fn as_any(&self) -> Option<&dyn std::any::Any> {
122 Some(self)
123 }
124}
125
126impl DynamicValue for f64 {
127 fn as_any(&self) -> Option<&dyn std::any::Any> {
128 Some(self)
129 }
130}
131
132impl DynamicValue for f32 {
133 fn as_any(&self) -> Option<&dyn std::any::Any> {
134 Some(self)
135 }
136}
137
138impl DynamicValue for bool {
139 fn as_any(&self) -> Option<&dyn std::any::Any> {
140 Some(self)
141 }
142}
143
144impl DynamicValue for char {
145 fn as_any(&self) -> Option<&dyn std::any::Any> {
146 Some(self)
147 }
148}
149
150impl DynamicValue for () {
151 fn as_any(&self) -> Option<&dyn std::any::Any> {
152 Some(self)
153 }
154}
155
156impl<T: DynamicValue + 'static> DynamicValue for Option<T> {
157 fn as_any(&self) -> Option<&dyn std::any::Any> {
158 Some(self)
159 }
160}
161
162#[derive(Clone, Copy, Debug)]
163pub struct Signal<T> {
164 id: (usize, usize),
165 _marker: std::marker::PhantomData<T>,
166}
167
168struct SignalValue {
169 value: Box<dyn DynamicValue>,
170}
171
172impl<T: DynamicValue + PartialEq + Clone + 'static> Signal<T> {
173 pub fn set(&self, value: T) {
174 let mut changed = false;
175 SIGNALS.with(|signals| {
177 if let Some(stored) = signals.borrow_mut().get_mut(&self.id) {
178 if let Some(should_update) = stored
180 .value
181 .as_any()
182 .and_then(|any| any.downcast_ref::<T>().and_then(|val| Some(val != &value)))
183 .or(Some(false))
184 {
185 if should_update {
186 *stored = SignalValue {
187 value: Box::new(value),
188 };
189 changed = true;
190 }
191 }
192 }
193 });
194
195 if changed {
196 SCOPE_SIGNAL_CHANGES.with(|changes| {
197 changes.borrow_mut().insert(self.id);
198 });
199 }
200 }
201
202 pub fn get(&self) -> T {
203 if let Some(current_scope) = get_current_scope() {
204 SIGNAL_DEPENDENCIES.with(|deps| {
205 let mut deps = deps.borrow_mut();
206 let scopes = deps.entry(self.id).or_insert_with(HashSet::new);
207 scopes.insert(current_scope);
208 });
209 }
210
211 SIGNALS
212 .with(|signals| {
213 if let Some(stored) = signals.borrow().get(&self.id) {
214 if let Some(parsed) = stored
215 .value
216 .as_any()
217 .and_then(|any| any.downcast_ref::<T>())
218 {
219 return Some(parsed.clone());
220 }
221 }
222 None
223 })
224 .unwrap()
225 }
226}
227
228#[derive(Debug)]
229pub enum SignalCreationError {
230 OutsideScope,
231}
232
233impl std::fmt::Display for SignalCreationError {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 match self {
236 SignalCreationError::OutsideScope => {
237 write!(f, "Signals can only be created within a scope context")
238 }
239 }
240 }
241}
242
243impl std::error::Error for SignalCreationError {}
244
245fn get_current_scope() -> Option<usize> {
246 CURRENT_SCOPE.with(|scope| *scope.borrow())
247}
248
249fn set_current_scope(scope_id: Option<usize>) {
250 CURRENT_SCOPE.with(|scope| {
251 *scope.borrow_mut() = scope_id;
252 });
253 if let Some(id) = scope_id {
254 SCOPE_STACK.with(|stack| {
255 let mut stack = stack.borrow_mut();
256 if !stack.contains(&id) {
257 stack.push(id);
258 }
259 });
260 }
261}
262
263fn get_next_signal_id_for_scope(scope_id: usize) -> usize {
264 SCOPE_SIGNAL_COUNTERS.with(|counters| {
265 let mut counters = counters.borrow_mut();
266 let counter = counters.entry(scope_id).or_insert(0);
267 *counter += 1;
268 *counter
269 })
270}
271
272fn reset_signal_counters(scope_id: usize) {
273 SCOPE_SIGNAL_COUNTERS.with(|counters| {
274 counters.borrow_mut().remove(&scope_id);
275 });
276}
277
278fn schedule_dependent_scopes_for_rerender(signal_id: (usize, usize)) {
279 let dependent_scopes = SIGNAL_DEPENDENCIES.with(|deps| {
280 if let Ok(deps) = deps.try_borrow() {
281 deps.get(&signal_id).cloned().unwrap_or_default()
282 } else {
283 HashSet::new()
284 }
285 });
286
287 PENDING_SCOPE_RENDERS.with(|pending| {
288 if let Ok(mut pending) = pending.try_borrow_mut() {
289 for scope_id in dependent_scopes {
290 pending.insert(scope_id);
291 }
292 }
293 });
294}
295
296fn process_pending_renders() {
297 loop {
298 let scopes_to_render = PENDING_SCOPE_RENDERS.with(|pending| {
299 if let Ok(mut pending) = pending.try_borrow_mut() {
300 if pending.is_empty() {
301 return Vec::new();
302 }
303 let scopes = pending.iter().copied().collect::<Vec<_>>();
304 pending.clear();
305 scopes
306 } else {
307 Vec::new()
308 }
309 });
310
311 if scopes_to_render.is_empty() {
312 break;
313 }
314
315 for scope_id in scopes_to_render {
316 render_scope(scope_id);
317 }
318 }
319}
320
321struct ScopeGuard {
322 previous_scope: Option<usize>,
323}
324
325impl Drop for ScopeGuard {
326 fn drop(&mut self) {
327 set_current_scope(self.previous_scope);
328 }
329}
330
331fn render_scope(scope_id: usize) -> Option<Node> {
332 let _guard = ScopeGuard {
334 previous_scope: get_current_scope(),
335 };
336
337 set_current_scope(Some(scope_id));
339
340 SIGNAL_DEPENDENCIES.with(|deps| {
342 if let Ok(mut deps) = deps.try_borrow_mut() {
343 for (_, scopes) in deps.iter_mut() {
344 scopes.remove(&scope_id);
345 }
346 }
347 });
348
349 RENDERING_SCOPE.with(|flag| {
351 if let Ok(mut flag) = flag.try_borrow_mut() {
352 *flag = true;
353 }
354 });
355
356 SCOPE_SIGNAL_CHANGES.with(|changes| {
357 if let Ok(mut changes) = changes.try_borrow_mut() {
358 changes.clear();
359 }
360 });
361
362 let scope_fn = SCOPE_FUNCTIONS.with(|scope_functions| {
364 let scope_functions = scope_functions.borrow();
365 if let Some(scope_fn) = scope_functions.get(&scope_id) {
366 return Some(scope_fn.clone());
367 }
368 return None;
369 });
370
371 let mut node = None;
372
373 if let Some(scope_fn) = scope_fn {
374 node = Some(scope_fn());
375 }
376
377 reset_signal_counters(scope_id);
378 run_scope_effects(scope_id);
379 reset_effect_counters(scope_id);
380
381 let signal_changes = SCOPE_SIGNAL_CHANGES.with(|stored_changes| {
383 if let Ok(mut changes) = stored_changes.try_borrow_mut() {
384 let collected = changes.clone();
385 changes.clear();
386 collected
387 } else {
388 HashSet::new()
389 }
390 });
391
392 RENDERING_SCOPE.with(|flag| {
393 if let Ok(mut flag) = flag.try_borrow_mut() {
394 *flag = false;
395 }
396 });
397
398 for signal_id in signal_changes {
400 schedule_dependent_scopes_for_rerender(signal_id);
401 }
402
403 node
404 }
406
407fn run_scope_effects(scope_id: usize) {
408 SCOPE_EFFECTS.with(|effects| {
409 let effects = effects.borrow();
410 for (&(effect_scope_id, _), effect) in effects.iter() {
411 if effect_scope_id == scope_id {
412 effect();
413 }
414 }
415 });
416}
417
418pub fn create_signal<T: DynamicValue + PartialEq + 'static>(initial_value: T) -> Signal<T> {
419 let scope_id = get_current_scope()
420 .ok_or(SignalCreationError::OutsideScope)
421 .unwrap();
422
423 let signal_id = get_next_signal_id_for_scope(scope_id);
424 let signal = Signal {
425 id: (scope_id, signal_id),
426 _marker: std::marker::PhantomData,
427 };
428
429 SIGNALS.with(|signals| {
430 if signals.borrow_mut().get_mut(&signal.id).is_none() {
431 signals.borrow_mut().insert(
432 signal.id,
433 SignalValue {
434 value: Box::new(initial_value),
435 },
436 );
437 }
438 });
439
440 signal
441}
442
443#[derive(Clone, Copy, Debug)]
444struct Effect {
445 id: (usize, usize),
446}
447
448fn get_next_effect_id_for_scope(scope_id: usize) -> usize {
449 SCOPE_EFFECT_COUNTERS.with(|counters| {
450 let mut counters = counters.borrow_mut();
451 let counter = counters.entry(scope_id).or_insert(0);
452 *counter += 1;
453 *counter
454 })
455}
456
457fn reset_effect_counters(scope_id: usize) {
458 SCOPE_EFFECT_COUNTERS.with(|counters| {
459 counters.borrow_mut().remove(&scope_id);
460 });
461}
462
463pub fn create_effect(effect: impl Fn() + Send + Sync + 'static) {
464 let scope_id = get_current_scope()
465 .ok_or(SignalCreationError::OutsideScope)
466 .unwrap();
467
468 let effect_id = get_next_effect_id_for_scope(scope_id);
469 let effect_struct = Effect {
470 id: (scope_id, effect_id),
471 };
472
473 SCOPE_EFFECTS.with(|effects| {
474 effects
475 .borrow_mut()
476 .insert(effect_struct.id, Box::new(effect));
477 });
478}
479
480pub fn run_scope(scope_fn: impl Fn() -> Node + Send + Sync + 'static) -> Option<Node> {
481 let scope_id = NEXT_SCOPE_ID.with(|id| {
483 if let Ok(mut id) = id.try_borrow_mut() {
484 let current = *id;
485 *id = current + 1;
486 current
487 } else {
488 panic!("Failed to get next scope ID")
489 }
490 });
491
492 SCOPE_FUNCTIONS.with(|scope_functions| {
494 let mut scope_functions = scope_functions.borrow_mut();
495 scope_functions.insert(scope_id, Arc::new(scope_fn));
496 });
497
498 let node = render_scope(scope_id);
500
501 process_pending_renders();
503
504 node
505}
506
507pub fn rerender_all_scopes() {
509 SCOPE_FUNCTIONS.with(|scope_functions| {
510 let scope_functions = scope_functions.borrow();
511 for scope_id in scope_functions.keys().cloned() {
512 render_scope(scope_id);
513 }
514 });
515
516 process_pending_renders();
517}
518
519#[cfg(test)]
520mod tests {
521 use super::*;
522 use std::sync::Arc;
523 use std::sync::atomic::{AtomicUsize, Ordering};
524
525 #[test]
526 fn test_nested_scopes() {
527 run_scope(|| {
528 let outer_signal = create_signal(0);
529
530 run_scope(move || {
531 let inner_signal = create_signal("hello");
532 assert!(inner_signal.get() == "hello");
533 outer_signal.set(42); Node::Empty
535 });
536
537 assert_eq!(outer_signal.get(), 42);
539
540 Node::Empty
541 });
542 }
543
544 #[test]
545 fn test_signal_and_effect_in_scope() {
546 run_scope(move || {
547 let effect_count = Arc::new(AtomicUsize::new(0));
548 let effect_count_clone = effect_count.clone();
549 let signal = create_signal(0);
550
551 create_effect(move || {
552 let _ = signal.get();
553 effect_count_clone.fetch_add(1, Ordering::SeqCst);
554 assert!(effect_count.load(Ordering::SeqCst) > 0);
556 signal.set(1);
558 });
559
560 Node::Empty
561 });
562 }
563
564 #[test]
565 fn test_multiple_signals_and_dependencies() {
566 run_scope(|| {
567 let signal1 = create_signal("hello");
568 let signal2 = create_signal(0);
569
570 create_effect(move || {
571 let str_val = signal1.get();
572 let num_val = signal2.get();
573
574 println!("Effect running with values: {}, {}", str_val, num_val);
575 });
576
577 signal1.set("world");
578 signal2.set(42);
579
580 assert_eq!(signal1.get(), "world");
582 assert_eq!(signal2.get(), 42);
583
584 Node::Empty
585 });
586 }
587}