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}