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
use std::{cell::RefCell, sync::Arc}; #[derive(Default)] pub struct Engine { current_reaction: RefCell<Option<Reaction>>, pub(crate) current_update: RefCell<Option<Update>>, } impl Engine { #[must_use] pub fn new() -> Self { Self::default() } pub(crate) fn track(&self, subscriptions: &Arc<RefCell<SubscriptionList>>) { let mut reaction = self.current_reaction.borrow_mut(); let reaction = match reaction.as_mut() { Some(reaction) => reaction, None => return, }; subscriptions .borrow_mut() .push(reaction.subscriptions.clone()); } pub fn batch(self: &Arc<Self>) -> Batch { let mut current_update = self.current_update.borrow_mut(); let root = current_update.is_none(); if !root { return Batch { engine: None }; } *current_update = Some(Update::new()); Batch { engine: Some(self.clone()), } } pub fn react(&self, mut f: impl FnMut() + 'static) { let mut current_reaction = self.current_reaction.borrow_mut(); assert!(current_reaction.is_none()); *current_reaction = Some(Reaction::new()); drop(current_reaction); f(); let mut current_reaction = self.current_reaction.borrow_mut(); let reaction = current_reaction.take().unwrap(); reaction .subscriptions .borrow_mut() .push(Arc::new(RefCell::new(f))); } } fn slow_pop_front<T>(xs: &mut Vec<T>) -> Option<T> { if xs.is_empty() { None } else { Some(xs.remove(0)) } } type SubscriptionList = Vec<Arc<RefCell<Vec<Subscription>>>>; type Subscription = Arc<RefCell<dyn FnMut()>>; struct Reaction { subscriptions: Arc<RefCell<Vec<Subscription>>>, } impl Reaction { pub fn new() -> Self { Reaction { subscriptions: Arc::new(RefCell::new(Vec::new())), } } } pub(crate) struct Update { updates: Vec<Box<dyn FnOnce()>>, } impl Update { pub fn new() -> Self { Update { updates: Vec::new(), } } } #[must_use] pub struct Batch { engine: Option<Arc<Engine>>, } impl Drop for Batch { fn drop(&mut self) { let engine = match &mut self.engine { Some(x) => x, None => return, }; loop { let head = { let mut update = engine.current_update.borrow_mut(); slow_pop_front(&mut update.as_mut().unwrap().updates) }; let head = match head { Some(x) => x, None => break, }; head(); } engine.current_update.borrow_mut().take().unwrap(); } } #[cfg(test)] mod tests { use crate::instance::{Atom, Engine}; use std::{cell::RefCell, sync::Arc}; #[test] fn react_simple() { let engine = Arc::new(Engine::new()); let atom = Atom::new(engine.clone(), 1); let sink = Arc::new(RefCell::new(Vec::new())); engine.react({ let atom = atom.clone(); let sink = sink.clone(); move || { sink.borrow_mut().push(*atom.get()); } }); assert_eq!(*sink.borrow(), [1]); atom.set(2); assert_eq!(*sink.borrow(), [1, 2]); } }