zerodds_ast_core/
observer.rs1use crate::change::Changeset;
13use alloc::sync::Arc;
14use alloc::vec::Vec;
15
16pub trait AstObserver: Send + Sync {
21 fn on_commit(&self, changeset: &Changeset);
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
27pub struct ObserverToken(u64);
28
29#[derive(Clone, Default)]
34pub struct ObserverRegistry {
35 entries: Vec<(ObserverToken, Arc<dyn AstObserver>)>,
36 next_token: u64,
37}
38
39impl ObserverRegistry {
40 #[must_use]
42 pub const fn new() -> Self {
43 Self {
44 entries: Vec::new(),
45 next_token: 0,
46 }
47 }
48
49 pub fn register(&mut self, observer: Arc<dyn AstObserver>) -> ObserverToken {
51 let token = ObserverToken(self.next_token);
52 self.next_token = self.next_token.wrapping_add(1);
53 self.entries.push((token, observer));
54 token
55 }
56
57 pub fn unregister(&mut self, token: ObserverToken) -> bool {
59 let before = self.entries.len();
60 self.entries.retain(|(candidate, _)| *candidate != token);
61 self.entries.len() != before
62 }
63
64 #[must_use]
66 pub fn len(&self) -> usize {
67 self.entries.len()
68 }
69
70 #[must_use]
72 pub fn is_empty(&self) -> bool {
73 self.entries.is_empty()
74 }
75
76 pub fn notify(&self, changeset: &Changeset) {
82 for (_, observer) in &self.entries {
83 observer.on_commit(changeset);
84 }
85 }
86}
87
88impl core::fmt::Debug for ObserverRegistry {
89 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
90 f.debug_struct("ObserverRegistry")
91 .field("len", &self.entries.len())
92 .finish()
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 #![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
100 use super::*;
101 use crate::change::{Change, NodeKindTag};
102 use crate::id::{IdSpace, NodeId};
103 use core::sync::atomic::{AtomicUsize, Ordering};
104
105 struct Counter {
106 changes: AtomicUsize,
107 calls: AtomicUsize,
108 }
109
110 impl Counter {
111 fn new() -> Self {
112 Self {
113 changes: AtomicUsize::new(0),
114 calls: AtomicUsize::new(0),
115 }
116 }
117 }
118
119 impl AstObserver for Counter {
120 fn on_commit(&self, changeset: &Changeset) {
121 self.changes.fetch_add(changeset.len(), Ordering::SeqCst);
122 self.calls.fetch_add(1, Ordering::SeqCst);
123 }
124 }
125
126 fn one_change_set() -> Changeset {
127 let mut cs = Changeset::new(1);
128 cs.push(Change::Created {
129 node: NodeId::from_parts(IdSpace::MODEL, 1),
130 kind: NodeKindTag(1),
131 });
132 cs
133 }
134
135 #[test]
136 fn every_registered_observer_is_notified() {
137 let mut registry = ObserverRegistry::new();
138 let a = Arc::new(Counter::new());
139 let b = Arc::new(Counter::new());
140 let _ = registry.register(a.clone());
141 let _ = registry.register(b.clone());
142
143 registry.notify(&one_change_set());
144
145 assert_eq!(a.changes.load(Ordering::SeqCst), 1);
146 assert_eq!(b.changes.load(Ordering::SeqCst), 1);
147 assert_eq!(registry.len(), 2);
148 }
149
150 #[test]
151 fn an_unregistered_observer_stops_receiving() {
152 let mut registry = ObserverRegistry::new();
153 let observer = Arc::new(Counter::new());
154 let token = registry.register(observer.clone());
155
156 registry.notify(&one_change_set());
157 assert!(registry.unregister(token));
158 registry.notify(&one_change_set());
159
160 assert_eq!(observer.calls.load(Ordering::SeqCst), 1);
161 assert_eq!(registry.len(), 0);
162 assert!(registry.is_empty());
163 }
164
165 #[test]
166 fn unregistering_twice_reports_the_second_call_as_a_no_op() {
167 let mut registry = ObserverRegistry::new();
168 let token = registry.register(Arc::new(Counter::new()));
169 assert!(registry.unregister(token));
170 assert!(!registry.unregister(token));
171 }
172}