silkenweb_reactive/
signal.rs1use std::{
3 cell::{Ref, RefCell, RefMut},
4 collections::HashSet,
5 hash::Hash,
6 rc::{self, Rc},
7};
8
9use crate::clone;
10
11type SharedState<T> = Rc<State<T>>;
12type WeakSharedState<T> = rc::Weak<State<T>>;
13
14pub struct Signal<T>(SharedState<T>);
26
27impl<T> Clone for Signal<T> {
28 fn clone(&self) -> Self {
29 Self(self.0.clone())
30 }
31}
32
33impl<T: 'static> Signal<T> {
34 pub fn new(initial: T) -> Self {
35 Self(Rc::new(State::new(initial)))
36 }
37
38 pub fn read(&self) -> ReadSignal<T> {
39 ReadSignal(self.0.clone())
40 }
41
42 pub fn write(&self) -> WriteSignal<T> {
43 WriteSignal(Rc::downgrade(&self.0))
44 }
45}
46
47pub struct ReadSignal<T>(SharedState<T>);
69
70impl<T> Clone for ReadSignal<T> {
71 fn clone(&self) -> Self {
72 Self(self.0.clone())
73 }
74}
75
76impl<T: 'static> ReadSignal<T> {
77 pub fn current(&self) -> Ref<T> {
79 self.0.current()
80 }
81
82 pub fn only_changes(&self) -> ReadSignal<T>
105 where
106 T: Clone + Eq,
107 {
108 let child = Signal::new(self.current().clone());
109
110 self.add_dependent(
111 &child,
112 Rc::new({
113 clone!(child);
114
115 move |new_value| {
116 if *child.read().current() != *new_value {
117 child.write().set(new_value.clone())
118 }
119 }
120 }),
121 );
122
123 child.read()
124 }
125
126 pub fn map<Output, Generate>(&self, generate: Generate) -> ReadSignal<Output>
131 where
132 Output: 'static,
133 Generate: 'static + Fn(&T) -> Output,
134 {
135 self.map_to(generate)
136 }
137
138 pub fn map_to<Output>(&self, receiver: impl SignalReceiver<T, Output>) -> ReadSignal<Output>
140 where
141 Output: 'static,
142 {
143 let child = Signal::new(receiver.receive(&self.current()));
144
145 self.add_dependent(
146 &child,
147 Rc::new({
148 let set_value = child.write();
149
150 move |new_value| set_value.set(receiver.receive(new_value))
151 }),
152 );
153
154 child.read()
155 }
156
157 fn add_dependent<U>(&self, child: &Signal<U>, dependent_callback: Rc<dyn Fn(&T)>) {
158 self.0
159 .dependents
160 .borrow_mut()
161 .insert(DependentCallback::new(&dependent_callback));
162 child
163 .0
164 .parents
165 .borrow_mut()
166 .push(Box::new(Parent::new(dependent_callback, &self)));
167 }
168}
169
170pub trait SignalReceiver<Input, Output>: 'static
175where
176 Input: 'static,
177 Output: 'static,
178{
179 fn receive(&self, x: &Input) -> Output;
180}
181
182impl<Input, Output, F> SignalReceiver<Input, Output> for F
183where
184 Input: 'static,
185 Output: 'static,
186 F: 'static + Fn(&Input) -> Output,
187{
188 fn receive(&self, x: &Input) -> Output {
189 self(x)
190 }
191}
192
193pub struct WriteSignal<T>(WeakSharedState<T>);
195
196impl<T> Clone for WriteSignal<T> {
197 fn clone(&self) -> Self {
198 Self(self.0.clone())
199 }
200}
201
202impl<T: 'static> WriteSignal<T> {
203 pub fn set(&self, new_value: T) {
205 if let Some(state) = self.0.upgrade() {
206 *state.current_mut() = new_value;
207 state.update_dependents();
208 }
209 }
210
211 pub fn replace(&self, f: impl 'static + FnOnce(&T) -> T) {
214 self.mutate(|x| *x = f(x));
215 }
216
217 pub fn mutate(&self, f: impl 'static + FnOnce(&mut T)) {
220 if let Some(state) = self.0.upgrade() {
221 f(&mut state.current_mut());
222 state.update_dependents();
223 }
224 }
225}
226
227pub trait ZipSignal<Generate> {
230 type Target;
232
233 fn map(&self, generate: Generate) -> ReadSignal<Self::Target>;
235}
236
237impl<T0, T1, U, Generate> ZipSignal<Generate> for (ReadSignal<T0>, ReadSignal<T1>)
238where
239 T0: 'static,
240 T1: 'static,
241 U: 'static,
242 Generate: 'static + Fn(&T0, &T1) -> U,
243{
244 type Target = U;
245
246 fn map(&self, generate: Generate) -> ReadSignal<Self::Target> {
247 let x0 = &self.0;
248 let x1 = &self.1;
249 let child = Signal::new(generate(&x0.current(), &x1.current()));
250 let generate0 = Rc::new(generate);
251 let generate1 = generate0.clone();
252
253 x0.add_dependent(
254 &child,
255 Rc::new({
256 let set_value = child.write();
257 clone!(x1);
258
259 move |new_value| set_value.set(generate0(new_value, &x1.current()))
260 }),
261 );
262
263 x1.add_dependent(
264 &child,
265 Rc::new({
266 let set_value = child.write();
267 clone!(x0);
268
269 move |new_value| set_value.set(generate1(&x0.current(), new_value))
270 }),
271 );
272
273 child.read()
274 }
275}
276
277struct State<T> {
278 current: RefCell<T>,
279 parents: RefCell<Vec<Box<dyn AnyParent>>>,
280 dependents: RefCell<HashSet<DependentCallback<T>>>,
281}
282
283impl<T: 'static> State<T> {
284 fn new(init: T) -> Self {
285 Self {
286 current: RefCell::new(init),
287 parents: RefCell::new(Vec::new()),
288 dependents: RefCell::new(HashSet::new()),
289 }
290 }
291
292 fn update_dependents(&self) {
293 let dependents = self.dependents.borrow().clone();
296
297 for dep in &dependents {
306 if let Some(f) = dep.0.upgrade() {
307 f(&self.current());
308 }
309 }
310 }
311
312 fn current(&self) -> Ref<T> {
313 self.current
314 .try_borrow()
315 .expect("Possible circular dependency")
316 }
317
318 fn current_mut(&self) -> RefMut<T> {
319 self.current
320 .try_borrow_mut()
321 .expect("Possible circular dependency")
322 }
323}
324
325trait AnyParent {}
326
327struct Parent<T> {
328 dependent_callback: Rc<dyn Fn(&T)>,
329 parent: Rc<State<T>>,
330}
331
332impl<T> Parent<T> {
333 fn new(dependent_callback: Rc<dyn Fn(&T)>, parent: &ReadSignal<T>) -> Self {
334 Self {
335 dependent_callback,
336 parent: parent.0.clone(),
337 }
338 }
339}
340
341impl<T> AnyParent for Parent<T> {}
342
343impl<T> Drop for Parent<T> {
344 fn drop(&mut self) {
345 let removed = self
346 .parent
347 .dependents
348 .borrow_mut()
349 .remove(&DependentCallback(Rc::downgrade(&self.dependent_callback)));
350 assert!(removed);
351 }
352}
353
354struct DependentCallback<T>(rc::Weak<dyn Fn(&T)>);
355
356impl<T> Clone for DependentCallback<T> {
357 fn clone(&self) -> Self {
358 Self(self.0.clone())
359 }
360}
361
362impl<T> DependentCallback<T> {
363 fn new(f: &Rc<dyn 'static + Fn(&T)>) -> Self {
364 Self(Rc::downgrade(f))
365 }
366
367 fn upgrade(&self) -> Rc<dyn 'static + Fn(&T)> {
368 self.0.upgrade().unwrap()
369 }
370}
371
372impl<T> PartialEq for DependentCallback<T> {
373 fn eq(&self, other: &Self) -> bool {
374 Rc::as_ptr(&self.upgrade()).cast::<()>() == Rc::as_ptr(&other.upgrade()).cast::<()>()
377 }
378}
379
380impl<T> Eq for DependentCallback<T> {}
381
382impl<T> Hash for DependentCallback<T> {
383 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
384 Rc::as_ptr(&self.upgrade()).hash(state);
385 }
386}