1use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
7
8#[derive(Debug, Clone)]
10pub enum SignalError {
11 SignalDropped,
13 CircularDependency,
15 InvalidState,
17}
18
19impl std::fmt::Display for SignalError {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 match self {
22 SignalError::SignalDropped => write!(f, "Signal has been dropped"),
23 SignalError::CircularDependency => write!(f, "Circular dependency detected"),
24 SignalError::InvalidState => write!(f, "Invalid state transition"),
25 }
26 }
27}
28
29impl std::error::Error for SignalError {}
30
31#[derive(Debug)]
36pub struct ReactiveScope {
37 }
39
40impl ReactiveScope {
41 pub fn new() -> Self {
43 Self {
44 }
46 }
47}
48
49impl Default for ReactiveScope {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55pub struct Signal<T> {
57 pub value: Arc<RwLock<T>>,
58 dirty: Arc<RwLock<bool>>,
59}
60
61unsafe impl<T: Send + Sync> Send for Signal<T> {}
63unsafe impl<T: Send + Sync> Sync for Signal<T> {}
64
65impl<T> Signal<T>
66where
67 T: Send + Sync + 'static,
68{
69 pub fn get(&self) -> RwLockReadGuard<T> {
71 self.value.read().unwrap()
73 }
74
75 pub fn get_mut(&self) -> RwLockWriteGuard<T> {
77 self.value.write().unwrap()
78 }
79
80 pub fn set(&self, value: T) -> Result<(), SignalError> {
82 {
83 let mut val = self.value.write().unwrap();
84 *val = value;
85 }
86
87 *self.dirty.write().unwrap() = true;
89
90 Ok(())
92 }
93
94 pub fn update<F>(&self, f: F) -> Result<(), SignalError>
96 where
97 F: FnOnce(&mut T),
98 {
99 {
100 let mut value = self.value.write().unwrap();
101 f(&mut *value);
102 }
103 self.set_dirty()
104 }
105
106 fn set_dirty(&self) -> Result<(), SignalError> {
107 *self.dirty.write().unwrap() = true;
108 Ok(())
109 }
110}
111
112pub struct Effect<F> {
114 callback: Mutex<Option<F>>,
115 dirty: Arc<RwLock<bool>>,
116}
117
118unsafe impl<F: Send + Sync> Send for Effect<F> {}
120unsafe impl<F: Send + Sync> Sync for Effect<F> {}
121
122impl Effect<Box<dyn FnMut() + Send + Sync + 'static>> {
123 pub fn run(&self) -> Result<(), SignalError> {
125 let should_run = {
127 let callback_ref = self.callback.lock().unwrap();
128 callback_ref.is_some()
129 };
130
131 if should_run {
132 let mut callback = self.callback.lock().unwrap().take().unwrap();
133 callback();
134 *self.callback.lock().unwrap() = Some(callback);
135 *self.dirty.write().unwrap() = false;
136 }
137 Ok(())
138 }
139}
140
141pub struct ReactiveComputed<T, F> {
143 value: Arc<RwLock<Option<T>>>,
144 compute_fn: Mutex<Option<F>>,
145 dirty: Arc<RwLock<bool>>,
146}
147
148unsafe impl<T: Send + Sync, F: Send + Sync> Send for ReactiveComputed<T, F> {}
150unsafe impl<T: Send + Sync, F: Send + Sync> Sync for ReactiveComputed<T, F> {}
151
152impl<T> ReactiveComputed<T, Box<dyn FnMut() -> T + Send + Sync + 'static>>
153where
154 T: Send + Sync + Clone + 'static,
155{
156 pub fn get(&self) -> Result<T, SignalError> {
158 if *self.dirty.read().unwrap() || self.value.read().unwrap().is_none() {
159 self.recompute()?;
160 }
161
162 self.value
163 .read()
164 .unwrap()
165 .clone()
166 .ok_or(SignalError::InvalidState)
167 }
168
169 fn recompute(&self) -> Result<(), SignalError> {
170 let should_compute = {
172 let compute_ref = self.compute_fn.lock().unwrap();
173 compute_ref.is_some()
174 };
175
176 if should_compute {
177 let mut compute_fn = self.compute_fn.lock().unwrap().take().unwrap();
178 let new_value = compute_fn();
179 *self.value.write().unwrap() = Some(new_value);
180 *self.compute_fn.lock().unwrap() = Some(compute_fn);
181 *self.dirty.write().unwrap() = false;
182 }
183 Ok(())
184 }
185}
186
187pub fn create_signal<T>(_scope: &ReactiveScope, initial_value: T) -> Signal<T>
189where
190 T: Send + Sync + 'static,
191{
192 Signal {
193 value: Arc::new(RwLock::new(initial_value)),
194 dirty: Arc::new(RwLock::new(false)),
195 }
196}
197
198pub fn create_effect<F>(
200 _scope: &ReactiveScope,
201 callback: F,
202) -> Effect<Box<dyn FnMut() + Send + Sync + 'static>>
203where
204 F: FnMut() + Send + Sync + 'static,
205{
206 let effect = Effect {
207 callback: Mutex::new(Some(
208 Box::new(callback) as Box<dyn FnMut() + Send + Sync + 'static>
209 )),
210 dirty: Arc::new(RwLock::new(true)), };
212
213 let _ = effect.run();
215 effect
216}
217
218pub fn create_computed<T, F>(
220 _scope: &ReactiveScope,
221 compute_fn: F,
222) -> ReactiveComputed<T, Box<dyn FnMut() -> T + Send + Sync + 'static>>
223where
224 F: FnMut() -> T + Send + Sync + 'static,
225 T: Send + Sync + Clone + 'static,
226{
227 ReactiveComputed {
228 value: Arc::new(RwLock::new(None)),
229 compute_fn: Mutex::new(Some(
230 Box::new(compute_fn) as Box<dyn FnMut() -> T + Send + Sync + 'static>
231 )),
232 dirty: Arc::new(RwLock::new(true)), }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn test_signal_creation_and_access() {
242 let scope = ReactiveScope::new();
243 let signal = create_signal(&scope, 42);
244
245 assert_eq!(*signal.get(), 42);
246 }
247
248 #[test]
249 fn test_signal_update() {
250 let scope = ReactiveScope::new();
251 let signal = create_signal(&scope, 10);
252
253 signal.update(|v| *v += 5).unwrap();
254 assert_eq!(*signal.get(), 15);
255 }
256
257 #[test]
258 fn test_effect_creation() {
259 let scope = ReactiveScope::new();
260 let counter = Arc::new(RwLock::new(0));
261 let counter_clone = counter.clone();
262
263 let _effect = create_effect(&scope, move || {
264 *counter_clone.write().unwrap() += 1;
265 });
266
267 assert_eq!(*counter.read().unwrap(), 1);
269 }
270
271 #[test]
272 fn test_computed_value() {
273 let scope = ReactiveScope::new();
274 let signal = create_signal(&scope, 5);
275 let signal_clone = signal.value.clone();
276
277 let computed = create_computed(&scope, move || *signal_clone.read().unwrap() * 2);
278
279 assert_eq!(computed.get().unwrap(), 10);
280 }
281}