hermes_five/utils/events.rs
1//! Defines Hermes-Five event manager system.
2
3use std::any::Any;
4use std::collections::HashMap;
5use std::fmt::{Debug, Formatter};
6use std::sync::atomic::{AtomicUsize, Ordering};
7use std::sync::Arc;
8
9use futures::future::BoxFuture;
10use futures::FutureExt;
11use parking_lot::Mutex;
12
13use crate::errors::Error;
14use crate::utils::task;
15
16type Callback =
17 dyn FnMut(Arc<dyn Any + Send + Sync>) -> BoxFuture<'static, Result<(), Error>> + Send;
18pub type EventHandler = usize;
19struct CallbackWrapper {
20 id: EventHandler,
21 callback: Box<Callback>,
22}
23type SyncedCallbackMap = Mutex<HashMap<String, Vec<CallbackWrapper>>>;
24
25#[derive(Clone, Default)]
26pub struct EventManager {
27 callbacks: Arc<SyncedCallbackMap>,
28 next_id: Arc<AtomicUsize>,
29}
30
31impl EventManager {
32 /// Register event handler for a specific event name.
33 ///
34 /// # Parameters
35 /// * `event` - The event name (any type that matches an `Into<String>`)
36 /// * `callback` - An async moved callback that accepts a single parameter as an argument.
37 /// The argument can be anything that might be both `Send + Sync`.
38 /// You can trick multiple parameters by turning them in a single tuple.
39 ///
40 /// # Return
41 /// Returns an EventHandler that can be used by the `unregister()` method.
42 ///
43 /// # Errors
44 /// If the event handler does not match the expected emitted event exactly it will fail silently.
45 /// That means if the tuple gave in the callback parameter does not exactly match the emit one
46 /// no handler will be called.
47 ///
48 /// # Example
49 ///
50 /// ```
51 /// use hermes_five::utils::EventManager;
52 /// use hermes_five::pause;
53 ///
54 /// #[hermes_five::runtime]
55 /// async fn main() {
56 /// // Instantiate an EventManager
57 /// let events: EventManager = Default::default();
58 ///
59 /// // Register various handlers for the same event.
60 /// events.on("ready", |name: String| async move { Ok(()) });
61 /// events.on("ready", |age: u8| async move { Ok(()) });
62 /// events.on("ready", |whatever: Vec<[u8;4]>| async move { Ok(()) });
63 /// events.on("ready", |(name, age): (&str, u8)| async move {
64 /// println!("Event handler with parameters: {} {}.", name, age);
65 /// pause!(1000);
66 /// println!("Event handler done");
67 /// Ok(())
68 /// });
69 ///
70 /// // Invoke handlers for "ready" event.
71 /// events.emit("ready", ("foo", 69u8));
72 ///
73 /// // None matching handler (because of parameters) will never be called.
74 /// events.emit("ready", ("bar"));
75 /// }
76 /// ```
77 pub fn on<S, F, T, Fut>(&self, event: S, mut callback: F) -> EventHandler
78 where
79 S: Into<String>,
80 T: 'static + Send + Sync + Clone,
81 F: FnMut(T) -> Fut + Send + 'static,
82 Fut: std::future::Future<Output = Result<(), Error>> + Send + 'static,
83 {
84 let event_name = event.into();
85 // Generate a unique ID.
86 let id = self.next_id.fetch_add(1, Ordering::Relaxed);
87 // Boxes the callback and downcast its parameter.
88 let boxed_callback =
89 Box::new(
90 move |arg: Arc<dyn Any + Send + Sync>| match arg.downcast::<T>() {
91 Ok(arg) => callback((*arg).clone()).boxed(),
92 Err(_) => Box::pin(async { Ok(()) }),
93 },
94 );
95
96 let wrapper = CallbackWrapper {
97 id,
98 callback: boxed_callback,
99 };
100
101 self.callbacks
102 .lock()
103 .entry(event_name)
104 .or_default()
105 .push(wrapper);
106
107 id
108 }
109
110 /// Invoke all event handlers registered for a specific event name.
111 /// Only the callback registered by the `on()` method and whose payload matches the declared
112 /// callback type will be called. All others will be silently skipped.
113 ///
114 /// # Parameters
115 /// * `event`: The event name (any type that matches an `Into<String>`)
116 /// * `payload`: The event payload (must be `'static + Send + Sync`)
117 /// The payload can be anything that might be both `Send + Sync`.
118 /// You can trick multiple parameters by turning them in a single tuple.
119 ///
120 /// # Example
121 ///
122 /// ```
123 /// use hermes_five::utils::EventManager;
124 ///
125 /// #[hermes_five::runtime]
126 /// async fn main() {
127 /// // Instantiate an EventManager
128 /// let events: EventManager = Default::default();
129 ///
130 /// // Register various handlers for the same event.
131 /// events.on("ready", |name: &str| async move {
132 /// println!("Callback 1");
133 /// Ok(())
134 /// });
135 /// events.on("ready", |age: u8| async move {
136 /// println!("Callback 2");
137 /// Ok(())
138 /// });
139 ///
140 /// // Invoke handlers for "ready" event matching &str parameter.
141 /// events.emit("ready", "foo");
142 /// // Invoke handlers for "ready" event matching u8 parameter.
143 /// events.emit("ready", 42);
144 ///
145 /// // No event registered for "nothing" event.
146 /// events.emit("nothing", ());
147 /// }
148 /// ```
149 pub fn emit<S, T>(&self, event: S, payload: T)
150 where
151 S: Into<String>,
152 T: 'static + Send + Sync,
153 {
154 let payload_any: Arc<dyn Any + Send + Sync> = Arc::new(payload);
155 if let Some(callbacks) = self.callbacks.lock().get_mut(&event.into()) {
156 for wrapper in callbacks.iter_mut() {
157 let payload_clone = payload_any.clone();
158 let future = (wrapper.callback)(payload_clone);
159 let _ = task::run(future);
160 }
161 }
162 }
163
164 /// Unregister a given handler if found.
165 ///
166 /// # Example
167 ///
168 /// ```
169 /// use hermes_five::utils::EventManager;
170 ///
171 /// #[hermes_five::runtime]
172 /// async fn main() {
173 /// // Instantiate an EventManager
174 /// let events: EventManager = Default::default();
175 ///
176 /// // Register various handlers for the same event.
177 /// let handler1 = events.on("ready", |age: u8| async move {
178 /// println!("Callback 1");
179 /// Ok(())
180 /// });
181 /// let handler2 = events.on("ready", |age: u8| async move {
182 /// println!("Callback 2");
183 /// Ok(())
184 /// });
185 ///
186 /// // Unregister handler 1.
187 /// events.unregister(handler1);
188 ///
189 /// // Invoke handlers for "ready" event matching u8 parameter.
190 /// // Only the callback2 remains to be called here.
191 /// events.emit("ready", 42);
192 /// }
193 /// ```
194 pub fn unregister(&self, handler: EventHandler) {
195 let _ = &self
196 .callbacks
197 .lock()
198 .values_mut()
199 .for_each(|v| v.retain(|cb| cb.id != handler));
200 }
201}
202
203impl Debug for EventManager {
204 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
205 match self.callbacks.lock().len() {
206 1 => write!(f, "EventManager: 1 registered callback"),
207 count => write!(f, "EventManager: {} registered callbacks", count),
208 }
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use std::sync::atomic::{AtomicBool, AtomicU8};
215
216 use crate::pause;
217
218 use super::*;
219
220 #[hermes_five_macros::test]
221 async fn test_register_and_emit_event() {
222 let events: EventManager = Default::default();
223 let payload = Arc::new(AtomicBool::new(false));
224
225 events.on("register", |flag: Arc<AtomicBool>| async move {
226 flag.store(true, Ordering::SeqCst);
227 Ok(())
228 });
229
230 events.emit("register", payload.clone());
231
232 pause!(100);
233 assert!(
234 payload.load(Ordering::SeqCst),
235 "The flag have been set by the triggered event."
236 );
237 }
238
239 #[hermes_five_macros::test]
240 async fn test_unregister_event_handler() {
241 let events: EventManager = Default::default();
242 let flag = Arc::new(AtomicBool::new(false));
243
244 let handler = events.on("unregister", |flag: Arc<AtomicBool>| async move {
245 flag.store(true, Ordering::SeqCst);
246 Ok(())
247 });
248
249 events.unregister(handler);
250 events.emit("unregister", flag.clone());
251
252 pause!(100);
253 assert!(
254 !flag.load(Ordering::SeqCst),
255 "The event was unregistered: the flag have not been set."
256 );
257 }
258
259 #[hermes_five_macros::test]
260 async fn test_multiple_handlers() {
261 let events: EventManager = Default::default();
262 let flag = Arc::new(AtomicUsize::new(0));
263
264 events.on("multiple", |flag: Arc<AtomicUsize>| async move {
265 let value = flag.load(Ordering::SeqCst);
266 flag.store(value + 1, Ordering::SeqCst);
267 Ok(())
268 });
269
270 events.on("multiple", |flag: Arc<AtomicUsize>| async move {
271 let value = flag.load(Ordering::SeqCst);
272 flag.store(value + 1, Ordering::SeqCst);
273 Ok(())
274 });
275
276 events.on(
277 "multiple",
278 |(_not_matching, flag): (u8, Arc<AtomicUsize>)| async move {
279 let value = flag.load(Ordering::SeqCst);
280 flag.store(value + 1, Ordering::SeqCst);
281 Ok(())
282 },
283 );
284
285 events.emit("multiple", flag.clone());
286
287 pause!(500);
288 assert_eq!(
289 flag.load(Ordering::SeqCst),
290 2,
291 "The flag have been increased by 2."
292 );
293 }
294
295 #[hermes_five_macros::test]
296 async fn test_event_with_complex_payload() {
297 let events: EventManager = Default::default();
298 let flag = Arc::new(AtomicU8::new(0));
299
300 events.on(
301 "payload",
302 |(number1, number2, container): (u8, u8, Arc<AtomicU8>)| async move {
303 container.store(number1 + number2, Ordering::SeqCst);
304 Ok(())
305 },
306 );
307 events.emit("payload", (42u8, 69u8, flag.clone()));
308
309 pause!(100);
310 assert_eq!(
311 flag.load(Ordering::SeqCst),
312 111,
313 "The complex flag has been properly received."
314 );
315 }
316
317 #[hermes_five_macros::test]
318 async fn test_no_handlers_for_event() {
319 let events: EventManager = Default::default();
320 let result = events.emit("no_event", ());
321 assert_eq!(result, (), "Nothing to do.");
322 }
323
324 #[test]
325 fn test_event_manager_debug() {
326 let events: EventManager = Default::default();
327 events.on("test", |_: ()| async move { Ok(()) });
328 assert_eq!(
329 format!("{:?}", events),
330 "EventManager: 1 registered callback"
331 );
332 events.on("test2", |_: ()| async move { Ok(()) });
333 assert_eq!(
334 format!("{:?}", events),
335 "EventManager: 2 registered callbacks"
336 );
337 }
338}