1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/// Creates a reactive effect from a closure (and optionally a dependency collector).
///
/// The `effect!` macro is a convenient wrapper around
/// [`reactive_cache::Effect::new`] and [`reactive_cache::Effect::new_with_deps`].
/// It allows you to quickly register a reactive effect that automatically tracks
/// dependencies and re-runs when they change.
///
/// # Forms
///
/// - `effect!(f)`
/// Equivalent to calling [`Effect::new(f)`]. In this form, dependencies are
/// automatically tracked while executing `f`.
///
/// - `effect!(f, deps)`
/// Equivalent to calling [`Effect::new_with_deps(f, deps)`]. In this form,
/// **dependency tracking is performed only when running `deps`**, not `f`.
/// The closure `f` will still be executed when dependencies change, but its
/// execution does **not** collect new dependencies.
///
/// # Requirements
///
/// - `f` must be a closure or function pointer that takes no arguments and returns `()`.
/// - `deps` (if provided) must also be a closure or function pointer taking no arguments and returning `()`.
///
/// # Examples
///
/// ```rust
/// use std::{cell::Cell, rc::Rc};
/// use reactive_cache::effect;
/// use reactive_cache::prelude::*;
/// use reactive_macros::signal;
///
/// signal!(static mut A: i32 = 1;);
///
/// // Track effect runs
/// let counter = Rc::new(Cell::new(0));
/// let counter_clone = counter.clone();
///
/// // `effect!(f)` form
/// let e = effect!(move || {
/// let _ = A().get(); // reading the signal
/// counter_clone.set(counter_clone.get() + 1); // increment effect counter
/// });
///
/// let ptr = Rc::into_raw(e); // actively leak to avoid implicitly dropping the effect
///
/// // Effect runs immediately upon creation
/// assert_eq!(counter.get(), 1);
///
/// // Changing A triggers the effect again
/// assert!(A().set(10));
/// assert_eq!(counter.get(), 2);
///
/// // Setting the same value does NOT trigger the effect
/// assert!(!A().set(10));
/// assert_eq!(counter.get(), 2);
///
/// // `effect!(f, deps)` form
/// let _ = effect!(
/// || println!("effect body"),
/// || println!("dependency collector")
/// );
/// ```
///
/// # SAFETY
///
/// The macro internally uses [`reactive_cache::Effect`], which relies on
/// `static` tracking and is **not thread-safe**. Only use in single-threaded contexts.
///
/// # Warning
///
/// **Do not set any signal that is part of the same effect chain.**
///
/// Effects automatically run whenever one of their dependent signals changes.
/// If an effect modifies a signal that it (directly or indirectly) observes,
/// it creates a circular dependency. This can lead to:
/// - an infinite loop of updates, or
/// - conflicting updates that the system cannot resolve.
///
/// In the general case, it is impossible to automatically determine whether
/// such an effect will ever terminate—this is essentially a version of the
/// halting problem. Therefore, you must ensure manually that effects do not
/// update signals within their own dependency chain.