epicenter/dispatchers/
sync.rs1use std::{
2 any::{Any, TypeId},
3 sync::RwLock,
4};
5
6use crate::Event;
7
8#[allow(clippy::type_complexity)]
9struct Listener {
10 event: TypeId,
11 handler: Box<dyn (FnMut(&mut dyn Any) -> Result<(), Error>)>,
12}
13
14pub struct Dispatcher {
15 listeners: RwLock<Vec<Listener>>,
16}
17
18impl Dispatcher {
19 #[must_use]
21 pub const fn new() -> Self {
22 Self {
23 listeners: RwLock::new(Vec::new()),
24 }
25 }
26
27 pub fn listen<Ev: Event + 'static>(
33 &mut self,
34 mut on_event: impl FnMut(&mut Ev) + 'static,
35 ) -> Result<(), Error> {
36 let mut listeners = self.listeners.write().map_err(|_| Error::LockPoisoned)?;
37
38 listeners.push(Listener {
39 event: TypeId::of::<Ev>(),
40 handler: Box::new(move |ev: &mut dyn Any| -> Result<(), Error> {
41 (on_event)(ev.downcast_mut().ok_or(Error::UnregisteredEvent)?);
42
43 Ok(())
44 }),
45 });
46
47 drop(listeners);
48
49 Ok(())
50 }
51
52 pub fn has_listeners<Ev: Event + 'static>(&self) -> Result<bool, Error> {
58 let listeners = self.listeners.read().map_err(|_| Error::LockPoisoned)?;
59
60 Ok(listeners.iter().any(|l| l.event == TypeId::of::<Ev>()))
61 }
62
63 #[allow(clippy::significant_drop_in_scrutinee)]
69 pub fn dispatch<Ev: Event + 'static>(&self, event: &mut Ev) -> Result<(), Error> {
70 for listener in self
71 .listeners
72 .write()
73 .map_err(|_| Error::LockPoisoned)?
74 .iter_mut()
75 .filter(|listener| listener.event == TypeId::of::<Ev>())
76 {
77 (listener.handler)(event)?;
78 }
79
80 Ok(())
81 }
82}
83
84impl Default for Dispatcher {
85 fn default() -> Self {
86 Self::new()
87 }
88}
89
90#[derive(Debug, thiserror::Error)]
91pub enum Error {
92 #[error("Event type is not registered with the dispatcher")]
94 UnregisteredEvent,
95
96 #[error("The listener lock is poisoned")]
98 LockPoisoned,
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[derive(Debug, PartialEq)]
106 struct OrderShipped {
107 order_id: u64,
108 }
109 impl Event for OrderShipped {}
110
111 #[test]
112 fn test_sync_dispatcher() {
113 let mut dispatcher = Dispatcher::new();
114
115 dispatcher
116 .listen(|event: &mut OrderShipped| {
117 assert_eq!(event.order_id, 123);
118 })
119 .unwrap();
120
121 dispatcher
122 .dispatch(&mut OrderShipped { order_id: 123 })
123 .unwrap();
124 }
125}