1use std::cell::{Cell, RefCell};
32use std::rc::{Rc, Weak};
33
34use crate::component::request_rebuild;
35
36type Listener = Rc<dyn Fn()>;
37
38#[derive(Default)]
39struct Listeners {
40 entries: RefCell<Vec<(u64, Listener)>>,
41 next: Cell<u64>,
42}
43
44pub trait Listenable {
46 fn add_listener(&self, listener: impl Fn() + 'static) -> Subscription;
49}
50
51#[must_use = "a subscription stops listening as soon as it is dropped"]
56pub struct Subscription {
57 listeners: Weak<Listeners>,
58 id: u64,
59}
60
61impl Subscription {
62 pub fn forget(self) {
64 std::mem::forget(self);
65 }
66}
67
68impl Drop for Subscription {
69 fn drop(&mut self) {
70 if let Some(listeners) = self.listeners.upgrade() {
71 let removed = {
72 let mut entries = listeners.entries.borrow_mut();
73 entries
74 .iter()
75 .position(|(id, _)| *id == self.id)
76 .map(|at| entries.remove(at))
77 };
78 drop(removed);
79 }
80 }
81}
82
83#[derive(Clone, Default)]
87pub struct ChangeNotifier {
88 listeners: Rc<Listeners>,
89}
90
91impl ChangeNotifier {
92 pub fn new() -> Self {
94 Self::default()
95 }
96
97 pub fn notify_listeners(&self) {
102 let told: Vec<Listener> = self
103 .listeners
104 .entries
105 .borrow()
106 .iter()
107 .map(|(_, listener)| listener.clone())
108 .collect();
109 for listener in told {
110 listener();
111 }
112 request_rebuild();
113 }
114
115 pub fn listener_count(&self) -> usize {
117 self.listeners.entries.borrow().len()
118 }
119}
120
121impl Listenable for ChangeNotifier {
122 fn add_listener(&self, listener: impl Fn() + 'static) -> Subscription {
123 let id = self.listeners.next.get();
124 self.listeners.next.set(id + 1);
125 self.listeners
126 .entries
127 .borrow_mut()
128 .push((id, Rc::new(listener)));
129 Subscription {
130 listeners: Rc::downgrade(&self.listeners),
131 id,
132 }
133 }
134}
135
136impl std::fmt::Debug for ChangeNotifier {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 write!(f, "ChangeNotifier({} listeners)", self.listener_count())
139 }
140}
141
142pub struct ValueNotifier<T> {
146 value: Rc<RefCell<T>>,
147 notifier: ChangeNotifier,
148}
149
150impl<T> Clone for ValueNotifier<T> {
151 fn clone(&self) -> Self {
152 Self {
153 value: self.value.clone(),
154 notifier: self.notifier.clone(),
155 }
156 }
157}
158
159impl<T: 'static> ValueNotifier<T> {
160 pub fn new(value: T) -> Self {
162 Self {
163 value: Rc::new(RefCell::new(value)),
164 notifier: ChangeNotifier::new(),
165 }
166 }
167
168 pub fn get(&self) -> T
170 where
171 T: Clone,
172 {
173 self.value.borrow().clone()
174 }
175
176 pub fn with<R>(&self, read: impl FnOnce(&T) -> R) -> R {
178 read(&self.value.borrow())
179 }
180
181 pub fn set(&self, value: T)
184 where
185 T: PartialEq,
186 {
187 if *self.value.borrow() == value {
188 return;
189 }
190 *self.value.borrow_mut() = value;
191 self.notifier.notify_listeners();
192 }
193
194 pub fn update(&self, change: impl FnOnce(&mut T)) {
196 change(&mut self.value.borrow_mut());
197 self.notifier.notify_listeners();
198 }
199}
200
201impl<T: Default + 'static> Default for ValueNotifier<T> {
202 fn default() -> Self {
203 Self::new(T::default())
204 }
205}
206
207impl<T> Listenable for ValueNotifier<T> {
208 fn add_listener(&self, listener: impl Fn() + 'static) -> Subscription {
209 self.notifier.add_listener(listener)
210 }
211}
212
213impl<T: std::fmt::Debug> std::fmt::Debug for ValueNotifier<T> {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 f.debug_tuple("ValueNotifier")
216 .field(&*self.value.borrow())
217 .finish()
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 use crate::take_rebuild_request;
225
226 #[test]
227 fn listeners_are_told_in_the_order_they_were_added() {
228 let notifier = ChangeNotifier::new();
229 let log = Rc::new(RefCell::new(Vec::new()));
230 let (a, b) = (log.clone(), log.clone());
231 let _a = notifier.add_listener(move || a.borrow_mut().push("a"));
232 let _b = notifier.add_listener(move || b.borrow_mut().push("b"));
233 notifier.notify_listeners();
234 assert_eq!(*log.borrow(), ["a", "b"]);
235 }
236
237 #[test]
238 fn a_dropped_subscription_stops_the_notifications() {
239 let notifier = ChangeNotifier::new();
240 let count = Rc::new(Cell::new(0));
241 let counted = count.clone();
242 let subscription = notifier.add_listener(move || counted.set(counted.get() + 1));
243 notifier.notify_listeners();
244 assert_eq!(notifier.listener_count(), 1);
245 drop(subscription);
246 assert_eq!(notifier.listener_count(), 0);
247 notifier.notify_listeners();
248 assert_eq!(count.get(), 1);
249 }
250
251 #[test]
252 fn a_forgotten_subscription_keeps_listening() {
253 let notifier = ChangeNotifier::new();
254 let count = Rc::new(Cell::new(0));
255 let counted = count.clone();
256 notifier
257 .add_listener(move || counted.set(counted.get() + 1))
258 .forget();
259 notifier.notify_listeners();
260 notifier.notify_listeners();
261 assert_eq!(count.get(), 2);
262 }
263
264 #[test]
265 fn a_listener_may_drop_another_during_the_notification() {
266 let notifier = ChangeNotifier::new();
267 let count = Rc::new(Cell::new(0));
268 let slot: Rc<RefCell<Option<Subscription>>> = Rc::default();
269 let counted = count.clone();
270 let victim = notifier.add_listener(move || counted.set(counted.get() + 1));
271 *slot.borrow_mut() = Some(victim);
272 let dropper = slot.clone();
273 let _first = notifier.add_listener(move || {
274 dropper.borrow_mut().take();
275 });
276 notifier.notify_listeners();
279 notifier.notify_listeners();
280 assert_eq!(count.get(), 1);
281 }
282
283 #[test]
284 fn a_value_notifier_tells_only_when_the_value_changes() {
285 let value = ValueNotifier::new(1);
286 let count = Rc::new(Cell::new(0));
287 let counted = count.clone();
288 let _listener = value.add_listener(move || counted.set(counted.get() + 1));
289 value.set(1);
290 assert_eq!(count.get(), 0, "the same value is not a change");
291 value.set(2);
292 value.update(|v| *v += 1);
293 assert_eq!((count.get(), value.get()), (2, 3));
294 }
295
296 #[test]
297 fn a_change_asks_for_a_rebuild() {
298 take_rebuild_request();
299 let value = ValueNotifier::new(String::new());
300 value.set("x".into());
301 assert!(take_rebuild_request());
302 value.set("x".into());
303 assert!(!take_rebuild_request(), "and no change asks for nothing");
304 }
305}