1use std::collections::VecDeque;
2
3use crate::theme::Theme;
4
5#[derive(Clone, Debug, PartialEq)]
7pub enum Event {
8 ThemeChanged(Theme),
10 PluginMessage {
12 from: String,
13 to: String,
14 action: String,
15 data: String,
16 },
17}
18
19pub type EventSubscriber = Box<dyn FnMut(&Event) + Send>;
21
22pub struct EventBus {
35 pending: VecDeque<Event>,
36 subscribers: Vec<EventSubscriber>,
37}
38
39const MAX_PENDING: usize = 1024;
40
41impl EventBus {
42 pub fn new() -> Self {
43 Self {
44 pending: VecDeque::new(),
45 subscribers: Vec::new(),
46 }
47 }
48
49 pub fn subscribe(&mut self, f: EventSubscriber) {
55 self.subscribers.push(f);
56 }
57
58 pub fn emit(&mut self, event: Event) {
62 for sub in &mut self.subscribers {
63 sub(&event);
64 }
65 if self.pending.len() >= MAX_PENDING {
66 self.pending.pop_front();
67 }
68 self.pending.push_back(event);
69 }
70
71 pub fn drain(&mut self) -> Vec<Event> {
73 std::mem::take(&mut self.pending).into_iter().collect()
74 }
75}
76
77impl std::fmt::Debug for EventBus {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 f.debug_struct("EventBus")
80 .field("pending", &self.pending)
81 .field(
82 "subscribers",
83 &format_args!("{} subscribers", self.subscribers.len()),
84 )
85 .finish()
86 }
87}
88
89impl Default for EventBus {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use std::sync::Mutex;
99
100 fn msg(from: &str, to: &str) -> Event {
101 Event::PluginMessage {
102 from: from.into(),
103 to: to.into(),
104 action: "test".into(),
105 data: "{}".into(),
106 }
107 }
108
109 #[test]
110 fn new_creates_empty_bus() {
111 let mut bus = EventBus::new();
112 assert!(bus.drain().is_empty());
113 }
114
115 #[test]
116 fn default_creates_empty_bus() {
117 let mut bus = EventBus::default();
118 assert!(bus.drain().is_empty());
119 }
120
121 #[test]
122 fn emit_and_drain_returns_event() {
123 let mut bus = EventBus::new();
124 bus.emit(msg("a", "b"));
125 let drained = bus.drain();
126 assert_eq!(drained.len(), 1);
127 assert_eq!(drained[0], msg("a", "b"));
128 }
129
130 #[test]
131 fn drain_returns_in_order() {
132 let mut bus = EventBus::new();
133 bus.emit(msg("a", "b"));
134 bus.emit(msg("c", "d"));
135 let drained = bus.drain();
136 assert_eq!(drained.len(), 2);
137 assert_eq!(drained[0], msg("a", "b"));
138 assert_eq!(drained[1], msg("c", "d"));
139 }
140
141 #[test]
142 fn drain_clears_queue() {
143 let mut bus = EventBus::new();
144 bus.emit(msg("a", "b"));
145 let first = bus.drain();
146 assert_eq!(first.len(), 1);
147 let second = bus.drain();
148 assert!(second.is_empty());
149 }
150
151 #[test]
152 fn drain_empty_returns_empty() {
153 let mut bus = EventBus::new();
154 assert!(bus.drain().is_empty());
155 assert!(bus.drain().is_empty());
156 }
157
158 #[test]
159 fn subscriber_receives_event() {
160 let mut bus = EventBus::new();
161 let count = std::sync::Arc::new(Mutex::new(0usize));
162 let c = count.clone();
163 bus.subscribe(Box::new(move |_| {
164 *c.lock().unwrap() += 1;
165 }));
166 bus.emit(msg("a", "b"));
167 assert_eq!(*count.lock().unwrap(), 1);
168 bus.emit(msg("c", "d"));
169 assert_eq!(*count.lock().unwrap(), 2);
170 }
171
172 #[test]
173 fn multiple_subscribers_all_called() {
174 let mut bus = EventBus::new();
175 let c1 = std::sync::Arc::new(Mutex::new(0usize));
176 let c2 = std::sync::Arc::new(Mutex::new(0usize));
177 let a1 = c1.clone();
178 let a2 = c2.clone();
179 bus.subscribe(Box::new(move |_| {
180 *a1.lock().unwrap() += 1;
181 }));
182 bus.subscribe(Box::new(move |_| {
183 *a2.lock().unwrap() += 1;
184 }));
185 bus.emit(msg("a", "b"));
186 assert_eq!(*c1.lock().unwrap(), 1);
187 assert_eq!(*c2.lock().unwrap(), 1);
188 }
189
190 #[test]
191 fn subscriber_receives_correct_event() {
192 let mut bus = EventBus::new();
193 let seen = std::sync::Arc::new(Mutex::new(String::new()));
194 let s = seen.clone();
195 bus.subscribe(Box::new(move |e| {
196 if let Event::PluginMessage { from, .. } = e {
197 *s.lock().unwrap() = from.clone();
198 }
199 }));
200 bus.emit(msg("hello", "world"));
201 assert_eq!(*seen.lock().unwrap(), "hello");
202 }
203
204 #[test]
205 fn max_pending_drops_oldest() {
206 let mut bus = EventBus::new();
207 for i in 0..super::MAX_PENDING {
208 bus.emit(msg(&format!("s{i}"), "t"));
209 }
210 bus.emit(msg("last", "t"));
211 let drained = bus.drain();
212 assert_eq!(drained.len(), super::MAX_PENDING);
213 if let Event::PluginMessage { from, .. } = &drained[0] {
214 assert_eq!(from, "s1");
215 } else {
216 panic!("expected PluginMessage");
217 }
218 if let Event::PluginMessage { from, .. } = &drained[drained.len() - 1] {
219 assert_eq!(from, "last");
220 } else {
221 panic!("expected PluginMessage");
222 }
223 }
224
225 #[test]
226 fn subscriber_called_before_queue_drain() {
227 let mut bus = EventBus::new();
228 let flag = std::sync::Arc::new(Mutex::new(false));
229 let f = flag.clone();
230 bus.subscribe(Box::new(move |_| {
231 *f.lock().unwrap() = true;
232 }));
233 bus.emit(msg("a", "b"));
234 assert!(*flag.lock().unwrap());
235 }
236
237 #[test]
238 fn debug_format() {
239 let mut bus = EventBus::new();
240 bus.subscribe(Box::new(|_| {}));
241 let s = format!("{:?}", bus);
242 assert!(s.contains("EventBus"));
243 assert!(s.contains("1 subscriber"));
244 }
245}