Skip to main content

counter/
counter.rs

1//! Counter demo: signal change → async task reaction → scope cleanup.
2//!
3//! Run with: `cargo run --example counter`
4
5use std::cell::Cell;
6use std::rc::Rc;
7
8use auralis_signal::{batch, Memo, Signal};
9use auralis_task::{init_flush_scheduler, init_time_source, ScheduleFlush, TaskScope, TimeSource};
10
11/// A synchronous scheduler — callbacks fire immediately.
12/// Good enough for CLI demos.  In a browser you'd use
13/// `requestAnimationFrame` or `queueMicrotask`.
14struct CliScheduler;
15impl ScheduleFlush for CliScheduler {
16    fn schedule(&self, callback: Box<dyn FnOnce()>) {
17        callback();
18    }
19}
20
21struct CliClock {
22    start: std::time::Instant,
23}
24impl CliClock {
25    fn new() -> Self {
26        Self {
27            start: std::time::Instant::now(),
28        }
29    }
30}
31impl TimeSource for CliClock {
32    fn now_ms(&self) -> u64 {
33        self.start.elapsed().as_millis() as u64
34    }
35}
36
37fn main() {
38    // One-time executor setup.
39    init_flush_scheduler(Rc::new(CliScheduler));
40    init_time_source(Rc::new(CliClock::new()));
41
42    // ---- Signal basics ----
43    println!("=== Signal basics ===");
44    let count = Signal::new(0);
45    println!("count = {}", count.read());
46    count.set(42);
47    println!("count = {}", count.read());
48
49    // ---- Memo (auto-tracking computed value) ----
50    println!("\n=== Memo ===");
51    let a = Signal::new(2);
52    let b = Signal::new(3);
53    let a2 = a.clone();
54    let b2 = b.clone();
55    let sum = Memo::new(move || a2.read() + b2.read());
56    println!("2 + 3 = {}", sum.read());
57    a.set(10);
58    println!("10 + 3 = {}", sum.read());
59
60    // ---- TaskScope: spawn a task that watches a signal ----
61    println!("\n=== TaskScope (scoped signal watcher) ===");
62    let messages = Signal::new(vec!["hello".to_string()]);
63
64    {
65        let scope = TaskScope::new();
66        let msgs = messages.clone();
67        scope.spawn(async move {
68            loop {
69                let val = msgs.changed().await;
70                println!("  task observed: {:?}", val);
71            }
72        });
73
74        messages.set(vec!["hello".to_string(), "world".to_string()]);
75        messages.set(vec![
76            "hello".to_string(),
77            "world".to_string(),
78            "!".to_string(),
79        ]);
80
81        // Explicit drop — cancels the task and cleans up resources.
82        drop(scope);
83    }
84    println!("(scope dropped — task and subscriptions cleaned up)");
85
86    // ---- Batch: multiple sets, one notification ----
87    println!("\n=== Batch ===");
88    let x = Signal::new(0);
89    let notifications = Rc::new(Cell::new(0u32));
90    let n = Rc::clone(&notifications);
91    let x2 = x.clone();
92
93    let scope = TaskScope::new();
94    scope.spawn(async move {
95        loop {
96            x2.changed().await;
97            n.set(n.get() + 1);
98        }
99    });
100
101    batch(|| {
102        x.set(1);
103        x.set(2);
104        x.set(3);
105    });
106    println!(
107        "After 3 sets in a batch: {} notification(s)",
108        notifications.get()
109    );
110    // Should be 1 — not 3.
111
112    println!("\nDone.");
113}