epicenter/dispatchers/
sync.rs

1use 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	/// Create a new event dispatcher instance.
20	#[must_use]
21	pub const fn new() -> Self {
22		Self {
23			listeners: RwLock::new(Vec::new()),
24		}
25	}
26
27	/// Register an event listener with the dispatcher.
28	///
29	/// # Errors
30	///
31	/// Returns an error if the listener lock is poisoned.
32	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	/// Determine if a given event has listeners.
53	///
54	/// # Errors
55	///
56	/// Returns an error if the listener lock is poisoned.
57	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	/// Fire an event and call the listeners.
64	///
65	/// # Errors
66	///
67	/// Returns an error if the event type is not registered with the dispatcher.
68	#[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	/// The event type is not registered with the dispatcher.
93	#[error("Event type is not registered with the dispatcher")]
94	UnregisteredEvent,
95
96	/// The listener lock is poisoned.
97	#[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}