1use std::cell::RefCell;
2use std::rc::Rc;
3
4use crate::effect::flush_effects;
5use crate::runtime::RUNTIME;
6
7type SubscriberId = usize;
8
9struct SignalInner<T> {
10 value: T,
11 subscribers: Vec<SubscriberId>,
12 version: u64,
13}
14
15pub struct ReadSignal<T> {
16 inner: Rc<RefCell<SignalInner<T>>>,
17}
18
19impl<T> Clone for ReadSignal<T> {
20 fn clone(&self) -> Self {
21 Self {
22 inner: self.inner.clone(),
23 }
24 }
25}
26
27pub struct WriteSignal<T> {
28 inner: Rc<RefCell<SignalInner<T>>>,
29}
30
31impl<T> Clone for WriteSignal<T> {
32 fn clone(&self) -> Self {
33 Self {
34 inner: self.inner.clone(),
35 }
36 }
37}
38
39pub fn create_signal<T>(value: T) -> (ReadSignal<T>, WriteSignal<T>) {
40 let inner = Rc::new(RefCell::new(SignalInner {
41 value,
42 subscribers: Vec::new(),
43 version: 0,
44 }));
45
46 (
47 ReadSignal {
48 inner: inner.clone(),
49 },
50 WriteSignal { inner },
51 )
52}
53
54impl<T: Clone> ReadSignal<T> {
55 pub fn get(&self) -> T {
56 self.track();
57 self.inner.borrow().value.clone()
58 }
59
60 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
61 self.track();
62 f(&self.inner.borrow().value)
63 }
64
65 pub fn get_untracked(&self) -> T {
66 self.inner.borrow().value.clone()
67 }
68
69 fn track(&self) {
70 RUNTIME.with(|rt| {
71 if let Some(effect_id) = rt.borrow().current_effect() {
72 let mut inner = self.inner.borrow_mut();
73 if !inner.subscribers.contains(&effect_id) {
74 inner.subscribers.push(effect_id);
75 }
76 }
77 });
78 }
79}
80
81impl<T> WriteSignal<T> {
82 pub fn set(&self, value: T) {
83 {
84 let mut inner = self.inner.borrow_mut();
85 inner.value = value;
86 inner.version += 1;
87 }
88 self.notify_subscribers();
89 }
90
91 pub fn update(&self, f: impl FnOnce(&mut T)) {
92 {
93 let mut inner = self.inner.borrow_mut();
94 f(&mut inner.value);
95 inner.version += 1;
96 }
97 self.notify_subscribers();
98 }
99
100 fn notify_subscribers(&self) {
101 let subscribers: Vec<SubscriberId> = { self.inner.borrow().subscribers.clone() };
102
103 let should_flush = RUNTIME.with(|rt| {
104 let mut rt = rt.borrow_mut();
105 for subscriber_id in subscribers {
106 rt.schedule_effect(subscriber_id);
107 }
108 !rt.is_batching()
109 });
110
111 if should_flush {
112 flush_effects();
113 }
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn test_signal_read_write() {
123 let (read, write) = create_signal(0);
124 assert_eq!(read.get_untracked(), 0);
125 write.set(5);
126 assert_eq!(read.get_untracked(), 5);
127 }
128
129 #[test]
130 fn test_signal_update() {
131 let (read, write) = create_signal(vec![1, 2]);
132 write.update(|v| v.push(3));
133 assert_eq!(read.get_untracked(), vec![1, 2, 3]);
134 }
135
136 #[test]
137 fn test_signal_with() {
138 let (read, _write) = create_signal(String::from("hello"));
139 let len = read.with(|s| s.len());
140 assert_eq!(len, 5);
141 }
142}