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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
//! # Tincan - A Fast, Lock-Free Reactive Primitives Library
//!
//! Tincan provides fine-grained reactivity with signals, computed values (memos),
//! and side effects. It's designed for building reactive applications with automatic
//! dependency tracking and efficient updates.
//!
//! ## Overview
//!
//! Tincan uses a **context object pattern** where all reactive primitives are created
//! through a [`Scope`]. This provides explicit ownership, automatic cleanup, and
//! lock-free performance for single-threaded contexts.
//!
//! ## Core Concepts
//!
//! - **[`Scope`]**: The reactive context that owns all reactive primitives
//! - **[`Signal`]**: Mutable reactive state that notifies observers when changed
//! - **[`Memo`]**: Computed values that cache results and only recompute when dependencies change
//! - **[`Effect`]**: Side effects that automatically re-run when dependencies change
//!
//! ## Quick Start
//!
//! ```rust
//! use tincan::Scope;
//!
//! // Create a reactive scope
//! let cx = Scope::new();
//!
//! // Create a signal (reactive state)
//! let count = cx.signal(0);
//!
//! // Create a memo (computed value)
//! let doubled = cx.memo({
//! let count = count.clone();
//! move || count.get() * 2
//! });
//!
//! // Create an effect (side effect)
//! let count_clone = count.clone();
//! let doubled_clone = doubled.clone();
//! cx.effect(move || {
//! println!("Count: {}, Doubled: {}", count_clone.get(), doubled_clone.get());
//! });
//! // Prints: Count: 0, Doubled: 0
//!
//! // Update the signal
//! count.set(5);
//! // Effect runs again, prints: Count: 5, Doubled: 10
//! ```
//!
//! ## Features
//!
//! ### Automatic Dependency Tracking
//!
//! Effects and memos automatically track which signals they read:
//!
//! ```rust
//! use tincan::Scope;
//!
//! let cx = Scope::new();
//! let first = cx.signal("John");
//! let last = cx.signal("Doe");
//!
//! let full_name = cx.memo({
//! let first = first.clone();
//! let last = last.clone();
//! move || format!("{} {}", first.get(), last.get())
//! });
//!
//! assert_eq!(full_name.get(), "John Doe");
//!
//! first.set("Jane");
//! assert_eq!(full_name.get(), "Jane Doe");
//! ```
//!
//! ### Efficient Updates
//!
//! Memos cache their values and only recompute when dependencies change:
//!
//! ```rust
//! use tincan::Scope;
//! use std::cell::Cell;
//! use std::rc::Rc;
//!
//! let cx = Scope::new();
//! let input = cx.signal(5);
//! let compute_count = Rc::new(Cell::new(0));
//!
//! let expensive = cx.memo({
//! let input = input.clone();
//! let compute_count = Rc::clone(&compute_count);
//! move || {
//! compute_count.set(compute_count.get() + 1);
//! input.get() * 2
//! }
//! });
//!
//! assert_eq!(expensive.get(), 10);
//! assert_eq!(compute_count.get(), 1);
//!
//! // Reading again uses cached value
//! assert_eq!(expensive.get(), 10);
//! assert_eq!(compute_count.get(), 1); // Not recomputed!
//!
//! // Only recomputes when input changes
//! input.set(10);
//! assert_eq!(expensive.get(), 20);
//! assert_eq!(compute_count.get(), 2); // Recomputed once
//! ```
//!
//! ### Isolation and Cleanup
//!
//! Scopes provide automatic cleanup and isolation:
//!
//! ```rust
//! use tincan::Scope;
//!
//! // Each scope is completely independent
//! let cx1 = Scope::new();
//! let signal1 = cx1.signal(1);
//!
//! let cx2 = Scope::new();
//! let signal2 = cx2.signal(2);
//!
//! // They don't interfere with each other
//! assert_eq!(signal1.get(), 1);
//! assert_eq!(signal2.get(), 2);
//!
//! // Dropping the scope cleans up all resources
//! drop(cx1);
//! // signal1 is now unusable (would panic if accessed)
//! ```
//!
//! ### Signal Combinators
//!
//! Transform and combine signals:
//!
//! ```rust
//! use tincan::Scope;
//!
//! let cx = Scope::new();
//! let celsius = cx.signal(0);
//!
//! // Map: Transform a signal
//! let fahrenheit = celsius.map(|c| c * 9 / 5 + 32);
//! assert_eq!(fahrenheit.get(), 32);
//!
//! celsius.set(100);
//! assert_eq!(fahrenheit.get(), 212);
//!
//! // Zip: Combine two signals
//! let width = cx.signal(10);
//! let height = cx.signal(5);
//! let area = width.clone().zip(height).map(|(w, h)| w * h);
//! assert_eq!(area.get(), 50);
//! ```
//!
//! ### Watching for Changes
//!
//! React to specific signal changes:
//!
//! ```rust
//! use tincan::Scope;
//! use std::cell::Cell;
//! use std::rc::Rc;
//!
//! let cx = Scope::new();
//! let count = cx.signal(0);
//! let call_count = Rc::new(Cell::new(0));
//!
//! let call_count_clone = Rc::clone(&call_count);
//! let _guard = count.watch(move |_value| {
//! call_count_clone.set(call_count_clone.get() + 1);
//! });
//!
//! assert_eq!(call_count.get(), 1); // Called immediately
//!
//! count.set(5);
//! assert_eq!(call_count.get(), 2); // Called on change
//! ```
//!
//! ## Testing
//!
//! Scopes make testing easy:
//!
//! ```rust
//! use tincan::Scope;
//!
//! #[test]
//! fn test_counter() {
//! let cx = Scope::new();
//! let count = cx.signal(0);
//!
//! count.set(42);
//! assert_eq!(count.get(), 42);
//! } // Scope dropped, everything cleaned up
//! ```
//!
//! ## Examples
//!
//! See the `examples/` directory for more complete examples:
//!
//! - `basic_signals.rs` - Simple signal operations
//! - `effects.rs` - Working with effects
//! - `memos.rs` - Computed values and caching
//!
pub use Effect;
pub use Memo;
pub use Scope;
pub use ;