1use std::any::{Any, TypeId};
33use std::collections::HashMap;
34use std::marker::PhantomData;
35use std::sync::Arc;
36
37use async_trait::async_trait;
38use serde::{de::DeserializeOwned, Serialize};
39
40use crate::db::Db;
41use crate::error::{CoreError, CoreResult};
42use crate::migration::DbBackend;
43use crate::module::ModuleId;
44use crate::registry::Registry;
45
46pub type EventError = Box<dyn std::error::Error + Send + Sync + 'static>;
49
50pub trait Event: Serialize + DeserializeOwned + Send + Sync + 'static {
56 const NAME: &'static str;
58}
59
60#[async_trait]
66pub trait Listener<E: Event>: Send + Sync + 'static {
67 async fn handle(&self, cx: &EventCx<'_>, event: &E) -> Result<(), EventError>;
68}
69
70pub struct EventCx<'a> {
74 db: &'a Db,
75 events: &'a Events,
76}
77
78impl EventCx<'_> {
79 pub fn db(&self) -> &Db {
81 self.db
82 }
83
84 pub fn backend(&self) -> DbBackend {
85 self.db.backend
86 }
87
88 pub fn events(&self) -> &Events {
90 self.events
91 }
92}
93
94#[async_trait]
97trait Erased: Send + Sync {
98 async fn handle(
99 &self,
100 cx: &EventCx<'_>,
101 event: &(dyn Any + Send + Sync),
102 ) -> Result<(), EventError>;
103}
104
105struct Typed<E, L> {
106 listener: L,
107 event: PhantomData<fn(E)>,
108}
109
110#[async_trait]
111impl<E: Event, L: Listener<E>> Erased for Typed<E, L> {
112 async fn handle(
113 &self,
114 cx: &EventCx<'_>,
115 event: &(dyn Any + Send + Sync),
116 ) -> Result<(), EventError> {
117 match event.downcast_ref::<E>() {
118 Some(event) => self.listener.handle(cx, event).await,
119 None => Ok(()),
122 }
123 }
124}
125
126pub struct EventListenerReg {
128 event: TypeId,
129 name: &'static str,
130 type_name: &'static str,
131 listener: Arc<dyn Erased>,
132}
133
134impl EventListenerReg {
135 pub fn on<E: Event>(listener: impl Listener<E>) -> Self {
137 Self {
138 event: TypeId::of::<E>(),
139 name: E::NAME,
140 type_name: std::any::type_name::<E>(),
141 listener: Arc::new(Typed {
142 listener,
143 event: PhantomData::<fn(E)>,
144 }),
145 }
146 }
147
148 pub fn event_name(&self) -> &'static str {
150 self.name
151 }
152}
153
154impl Registry {
155 pub fn listen<E: Event>(&mut self, listener: impl Listener<E>) {
163 self.add(EventListenerReg::on::<E>(listener));
164 }
165}
166
167struct Bound {
168 owner: Option<ModuleId>,
169 listener: Arc<dyn Erased>,
170}
171
172struct Inner {
173 db: Db,
174 by_event: HashMap<TypeId, Vec<Bound>>,
175}
176
177#[derive(Clone)]
179pub struct Events {
180 inner: Arc<Inner>,
181}
182
183impl Events {
184 pub fn new(db: Db) -> Self {
186 Self {
187 inner: Arc::new(Inner {
188 db,
189 by_event: HashMap::new(),
190 }),
191 }
192 }
193
194 pub fn builder(db: Db) -> EventsBuilder {
197 EventsBuilder {
198 db,
199 listeners: Vec::new(),
200 }
201 }
202
203 pub async fn emit<E: Event>(&self, event: &E) {
210 let Some(listeners) = self.inner.by_event.get(&TypeId::of::<E>()) else {
211 tracing::debug!(event = E::NAME, listeners = 0, "event emitted");
212 return;
213 };
214 tracing::debug!(
215 event = E::NAME,
216 listeners = listeners.len(),
217 "event emitted"
218 );
219 let cx = EventCx {
220 db: &self.inner.db,
221 events: self,
222 };
223 for bound in listeners {
224 if let Err(error) = bound.listener.handle(&cx, event).await {
225 tracing::error!(
226 event = E::NAME,
227 module = bound.owner.map(|m| m.as_str()).unwrap_or("application"),
228 error = %error,
229 "an event listener failed"
230 );
231 }
232 }
233 }
234
235 pub fn listeners<E: Event>(&self) -> usize {
237 self.inner
238 .by_event
239 .get(&TypeId::of::<E>())
240 .map_or(0, Vec::len)
241 }
242}
243
244pub struct EventsBuilder {
246 db: Db,
247 listeners: Vec<(Option<ModuleId>, EventListenerReg)>,
248}
249
250impl EventsBuilder {
251 pub fn listen<E: Event>(mut self, listener: impl Listener<E>) -> Self {
253 self.listeners
254 .push((None, EventListenerReg::on::<E>(listener)));
255 self
256 }
257
258 pub fn registered(mut self, owner: ModuleId, listener: EventListenerReg) -> Self {
261 self.listeners.push((Some(owner), listener));
262 self
263 }
264
265 pub fn build(self) -> CoreResult<Events> {
267 let mut named: HashMap<&'static str, (TypeId, &'static str)> = HashMap::new();
268 let mut by_event: HashMap<TypeId, Vec<Bound>> = HashMap::new();
269 for (owner, reg) in self.listeners {
270 let (event, type_name) = *named.entry(reg.name).or_insert((reg.event, reg.type_name));
271 if event != reg.event {
272 return Err(CoreError::Config(format!(
273 "event name '{}' is declared by both {} and {}",
274 reg.name, type_name, reg.type_name
275 )));
276 }
277 by_event.entry(reg.event).or_default().push(Bound {
278 owner,
279 listener: reg.listener,
280 });
281 }
282 Ok(Events {
283 inner: Arc::new(Inner {
284 db: self.db,
285 by_event,
286 }),
287 })
288 }
289}
290
291#[cfg(all(test, feature = "testing"))]
292mod tests {
293 use super::*;
294 use crate::testing::connect_test;
295 use serde::Deserialize;
296 use std::sync::Mutex;
297
298 #[derive(Serialize, Deserialize)]
299 struct Published {
300 article_id: i64,
301 }
302
303 impl Event for Published {
304 const NAME: &'static str = "acme.blog.published";
305 }
306
307 #[derive(Serialize, Deserialize)]
308 struct Archived {
309 article_id: i64,
310 }
311
312 impl Event for Archived {
313 const NAME: &'static str = "acme.blog.archived";
314 }
315
316 type Heard = Arc<Mutex<Vec<String>>>;
317
318 struct Note(&'static str, Heard);
320
321 #[async_trait]
322 impl Listener<Published> for Note {
323 async fn handle(&self, _cx: &EventCx<'_>, event: &Published) -> Result<(), EventError> {
324 self.1
325 .lock()
326 .unwrap()
327 .push(format!("{} published {}", self.0, event.article_id));
328 Ok(())
329 }
330 }
331
332 #[async_trait]
333 impl Listener<Archived> for Note {
334 async fn handle(&self, _cx: &EventCx<'_>, event: &Archived) -> Result<(), EventError> {
335 self.1
336 .lock()
337 .unwrap()
338 .push(format!("{} archived {}", self.0, event.article_id));
339 Ok(())
340 }
341 }
342
343 struct Fails;
344
345 #[async_trait]
346 impl Listener<Published> for Fails {
347 async fn handle(&self, _cx: &EventCx<'_>, _event: &Published) -> Result<(), EventError> {
348 Err("the mail server is away".into())
349 }
350 }
351
352 struct ArchiveAtOnce;
355
356 #[async_trait]
357 impl Listener<Published> for ArchiveAtOnce {
358 async fn handle(&self, cx: &EventCx<'_>, event: &Published) -> Result<(), EventError> {
359 cx.events()
360 .emit(&Archived {
361 article_id: event.article_id,
362 })
363 .await;
364 Ok(())
365 }
366 }
367
368 fn heard() -> Heard {
369 Arc::new(Mutex::new(Vec::new()))
370 }
371
372 #[tokio::test]
373 async fn listeners_hear_their_own_event_in_the_order_registered() {
374 let (db, _guard) = connect_test(&[]).await;
375 let log = heard();
376 let events = Events::builder(db)
377 .listen::<Published>(Note("first", log.clone()))
378 .listen::<Archived>(Note("other", log.clone()))
379 .listen::<Published>(Note("second", log.clone()))
380 .build()
381 .unwrap();
382
383 events.emit(&Published { article_id: 7 }).await;
384
385 assert_eq!(
386 *log.lock().unwrap(),
387 ["first published 7", "second published 7"]
388 );
389 assert_eq!(events.listeners::<Published>(), 2);
390 assert_eq!(events.listeners::<Archived>(), 1);
391 }
392
393 #[tokio::test]
394 async fn a_failing_listener_does_not_stop_the_next() {
395 let (db, _guard) = connect_test(&[]).await;
396 let log = heard();
397 let events = Events::builder(db)
398 .listen::<Published>(Fails)
399 .listen::<Published>(Note("after", log.clone()))
400 .build()
401 .unwrap();
402
403 events.emit(&Published { article_id: 3 }).await;
404
405 assert_eq!(*log.lock().unwrap(), ["after published 3"]);
406 }
407
408 #[tokio::test]
409 async fn a_listener_announces_a_fact_of_its_own() {
410 let (db, _guard) = connect_test(&[]).await;
411 let log = heard();
412 let events = Events::builder(db)
413 .listen::<Published>(ArchiveAtOnce)
414 .listen::<Archived>(Note("chained", log.clone()))
415 .build()
416 .unwrap();
417
418 events.emit(&Published { article_id: 9 }).await;
419
420 assert_eq!(*log.lock().unwrap(), ["chained archived 9"]);
421 }
422
423 #[tokio::test]
424 async fn an_event_nobody_hears_is_emitted_quietly() {
425 let (db, _guard) = connect_test(&[]).await;
426 let events = Events::new(db);
427 events.emit(&Published { article_id: 1 }).await;
428 assert_eq!(events.listeners::<Published>(), 0);
429 }
430
431 #[tokio::test]
432 async fn one_name_for_two_events_is_refused() {
433 #[derive(Serialize, Deserialize)]
434 struct Imposter;
435 impl Event for Imposter {
436 const NAME: &'static str = "acme.blog.published";
437 }
438 struct Quiet;
439 #[async_trait]
440 impl Listener<Imposter> for Quiet {
441 async fn handle(&self, _cx: &EventCx<'_>, _event: &Imposter) -> Result<(), EventError> {
442 Ok(())
443 }
444 }
445
446 let (db, _guard) = connect_test(&[]).await;
447 let refused = Events::builder(db)
448 .listen::<Published>(Note("first", heard()))
449 .listen::<Imposter>(Quiet)
450 .build();
451
452 let Err(CoreError::Config(message)) = refused else {
453 panic!("two types under one name must be refused");
454 };
455 assert!(message.contains("acme.blog.published"), "{message}");
456 assert!(message.contains("Imposter"), "{message}");
457 }
458
459 #[tokio::test]
460 async fn a_module_contributes_a_listener_through_the_registry() {
461 let (db, _guard) = connect_test(&[]).await;
462 let log = heard();
463 let mut registry = Registry::new();
464 registry.set_owner(ModuleId::new("acme.blog"));
465 registry.listen::<Published>(Note("module", log.clone()));
466
467 let mut builder = Events::builder(db);
468 for (owner, reg) in registry.take_owned::<EventListenerReg>() {
469 assert_eq!(reg.event_name(), "acme.blog.published");
470 builder = builder.registered(owner, reg);
471 }
472 let events = builder.build().unwrap();
473 events.emit(&Published { article_id: 4 }).await;
474
475 assert_eq!(*log.lock().unwrap(), ["module published 4"]);
476 }
477}