Skip to content

Commit 9182184

Browse files
committed
Add documentation and comments
1 parent ee613ac commit 9182184

File tree

6 files changed

+321
-4
lines changed

6 files changed

+321
-4
lines changed

cache/src/effect.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
use std::rc::Rc;
22

3+
/// A reactive effect that runs a closure whenever its dependencies change.
4+
///
5+
/// `Effect<F>` behaves similarly to an "event listener" or a callback,
6+
/// but it is automatically tied to any signals or memos it reads during execution.
7+
/// When those dependencies change, the effect will re-run.
8+
///
9+
/// Note: The closure runs **immediately upon creation** via `Effect::wrap`,
10+
/// so the effect is always initialized with an up-to-date value.
11+
///
12+
/// In short:
13+
/// - Like a callback: wraps a closure of type `F` and runs it.
14+
/// - Adds tracking: automatically re-runs when dependent signals change.
15+
/// - Runs once immediately at creation.
16+
///
17+
/// # Type Parameters
18+
///
19+
/// - `F`: The closure type wrapped by this effect. Must implement `Fn()`.
20+
/// The closure is executed immediately upon creation and tracked for reactive updates.
321
pub struct Effect<F>
422
where
523
F: Fn(),
@@ -11,6 +29,28 @@ impl<F> Effect<F>
1129
where
1230
F: Fn(),
1331
{
32+
/// Creates a new `Effect`, wrapping the provided closure
33+
/// and running it immediately for dependency tracking.
34+
///
35+
/// Returns an `Rc<dyn IEffect>` so the effect can be stored and shared
36+
/// as a non-generic trait object.
37+
///
38+
/// # Examples
39+
///
40+
/// ```
41+
/// use std::{cell::Cell, rc::Rc};
42+
/// use reactive_cache::Effect;
43+
///
44+
/// let counter = Rc::new(Cell::new(0));
45+
/// let c_clone = counter.clone();
46+
///
47+
/// let effect = Effect::new(move || {
48+
/// // This closure runs immediately
49+
/// c_clone.set(c_clone.get() + 1);
50+
/// });
51+
///
52+
/// assert_eq!(counter.get(), 1);
53+
/// ```
1454
#[allow(clippy::new_ret_no_self)]
1555
pub fn new(f: F) -> Rc<dyn IEffect>
1656
where
@@ -26,7 +66,16 @@ where
2666
}
2767
}
2868

69+
/// A non-generic trait for reactive effects.
70+
///
71+
/// `IEffect` serves as a type-erased trait for `Effect<F>` instances.
72+
/// By implementing `IEffect`, an `Effect<F>` can be stored as `Rc<dyn IEffect>`
73+
/// regardless of the specific closure type `F`. This allows the reactive system
74+
/// to manage multiple effects uniformly without exposing the generic type.
2975
pub trait IEffect {
76+
/// Runs the effect closure.
77+
///
78+
/// Typically called by the reactive system when dependencies change.
3079
fn run(&self);
3180
}
3281

cache/src/memo.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
use crate::{Observable, call_stack, remove_from_cache, store_in_cache, touch};
22

3+
/// A memoized reactive computation that caches its result and tracks dependencies.
4+
///
5+
/// `Memo<T, F>` behaves similarly to a computed property: it stores the result of a closure
6+
/// and only recomputes when its dependencies change. Other signals or effects that access
7+
/// the memo will automatically be tracked.
8+
///
9+
/// In short:
10+
/// - Like a computed property: returns a cached value derived from other signals.
11+
/// - Adds tracking: recomputes only when dependencies are invalidated.
12+
///
13+
/// # Type Parameters
14+
///
15+
/// - `T`: The result type of the computation. Must implement `Clone`.
16+
/// - `F`: The closure type that computes the value. Must implement `Fn() -> T`.
317
pub struct Memo<T, F>
418
where
519
F: Fn() -> T,
@@ -22,13 +36,35 @@ impl<T, F> Memo<T, F>
2236
where
2337
F: Fn() -> T,
2438
{
39+
/// Creates a new `Memo` wrapping the provided closure.
40+
///
41+
/// # Examples
42+
///
43+
/// ```
44+
/// use reactive_cache::Memo;
45+
///
46+
/// let memo = Memo::new(|| 10);
47+
/// ```
2548
pub fn new(f: F) -> Self {
2649
Memo {
2750
f,
2851
dependents: vec![],
2952
}
3053
}
3154

55+
/// Returns the memoized value, recomputing it only if necessary.
56+
///
57+
/// During the computation, dependencies are tracked for reactive updates.
58+
///
59+
/// # Examples
60+
///
61+
/// ```
62+
/// use once_cell::unsync::Lazy;
63+
/// use reactive_cache::Memo;
64+
///
65+
/// static mut MEMO: Lazy<Memo<i32, fn() -> i32>> = Lazy::new(|| Memo::new(|| 5));
66+
/// assert_eq!(unsafe { (*MEMO).get() }, 5);
67+
/// ```
3268
pub fn get(&'static mut self) -> T
3369
where
3470
T: Clone,

cache/src/signal.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,34 @@ use std::{
55

66
use crate::{IEffect, Observable, call_stack};
77

8+
/// A reactive signal that holds a value, tracks dependencies, and triggers effects.
9+
///
10+
/// `Signal<T>` behaves similarly to a traditional "Property" (getter/setter),
11+
/// but on top of that, it automatically tracks which reactive computations
12+
/// or effects access it. When its value changes, all dependent effects
13+
/// are automatically re-run.
14+
///
15+
/// In short:
16+
/// - Like a Property: provides `get()` and `set()` for accessing and updating the value.
17+
/// - Adds tracking: automatically records dependencies when read inside reactive contexts,
18+
/// and automatically triggers dependent `Effect`s when updated.
19+
///
20+
/// # Type Parameters
21+
///
22+
/// - `T`: The type of the value stored in the signal. Must implement `Eq + Default`.
823
pub struct Signal<T> {
924
value: RefCell<T>,
1025
dependents: RefCell<Vec<&'static dyn Observable>>,
1126
effects: RefCell<Vec<Weak<dyn IEffect>>>,
1227
}
1328

1429
impl<T> Signal<T> {
30+
/// Invalidates all dependent observables.
1531
fn invalidate(&self) {
1632
self.dependents.borrow().iter().for_each(|d| d.invalidate());
1733
}
1834

35+
/// Runs all dependent effects.
1936
fn flush_effects(&self) {
2037
self.effects.borrow_mut().retain(|w| {
2138
if let Some(e) = w.upgrade() {
@@ -37,6 +54,21 @@ impl<T> Signal<T> {
3754
self.invalidate()
3855
}
3956

57+
/// Creates a new `Signal` with the given initial value.
58+
///
59+
/// If `None` is provided, `T::default()` is used.
60+
///
61+
/// # Examples
62+
///
63+
/// ```
64+
/// use reactive_cache::Signal;
65+
///
66+
/// let signal = Signal::new(Some(10));
67+
/// assert_eq!(*signal.get(), 10);
68+
///
69+
/// let default_signal: Signal<i32> = Signal::new(None);
70+
/// assert_eq!(*default_signal.get(), 0);
71+
/// ```
4072
pub fn new(value: Option<T>) -> Self
4173
where
4274
T: Default,
@@ -48,7 +80,18 @@ impl<T> Signal<T> {
4880
}
4981
}
5082

83+
/// Gets a reference to the current value, tracking dependencies and effects if inside a reactive context.
84+
///
85+
/// # Examples
86+
///
87+
/// ```
88+
/// use reactive_cache::Signal;
89+
///
90+
/// let signal = Signal::new(Some(42));
91+
/// assert_eq!(*signal.get(), 42);
92+
/// ```
5193
pub fn get(&self) -> Ref<'_, T> {
94+
// Track observables in the call stack
5295
if let Some(last) = call_stack::last()
5396
&& !self
5497
.dependents
@@ -59,6 +102,7 @@ impl<T> Signal<T> {
59102
self.dependents.borrow_mut().push(*last);
60103
}
61104

105+
// Track effects in the call stack
62106
if let Some(e) = call_stack::creating_effect_peak()
63107
&& !self.effects.borrow().iter().any(|w| Weak::ptr_eq(w, &e))
64108
{
@@ -68,6 +112,22 @@ impl<T> Signal<T> {
68112
self.value.borrow()
69113
}
70114

115+
/// Sets the value of the signal.
116+
///
117+
/// Returns `true` if the value changed and dependent effects were triggered.
118+
///
119+
/// # Examples
120+
///
121+
/// ```
122+
/// use reactive_cache::Signal;
123+
///
124+
/// let signal = Signal::new(Some(5));
125+
/// assert_eq!(signal.set(10), true);
126+
/// assert_eq!(*signal.get(), 10);
127+
///
128+
/// // Setting to the same value returns false
129+
/// assert_eq!(signal.set(10), false);
130+
/// ```
71131
pub fn set(&self, value: T) -> bool
72132
where
73133
T: Eq,

0 commit comments

Comments
 (0)