Skip to main content

sova_core/
events.rs

1//! In-process application events (`EventBus`).
2
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5use std::sync::{Arc, Mutex};
6
7/// Typed application event.
8pub trait Event: Send + Sync + 'static {
9    fn name(&self) -> &'static str;
10}
11
12type DynListener = Arc<dyn Fn(&dyn Any) + Send + Sync>;
13
14/// Sync event bus: listeners run in the dispatching task (order of registration).
15#[derive(Clone, Default)]
16pub struct EventBus {
17    inner: Arc<Mutex<HashMap<TypeId, Vec<DynListener>>>>,
18}
19
20impl EventBus {
21    pub fn new() -> Self {
22        Self::default()
23    }
24
25    /// Register a typed listener. Multiple listeners per event type are allowed.
26    pub fn listen<E, F>(&self, f: F)
27    where
28        E: Event,
29        F: Fn(&E) + Send + Sync + 'static,
30    {
31        let mut map = self.inner.lock().expect("EventBus");
32        map.entry(TypeId::of::<E>()).or_default().push(Arc::new(move |any| {
33            if let Some(e) = any.downcast_ref::<E>() {
34                f(e);
35            }
36        }));
37    }
38
39    /// Dispatch `event` to all listeners for `E` (registration order).
40    pub fn dispatch<E: Event>(&self, event: E) {
41        let listeners = {
42            let map = self.inner.lock().expect("EventBus");
43            map.get(&TypeId::of::<E>()).cloned().unwrap_or_default()
44        };
45        for listener in &listeners {
46            listener(&event);
47        }
48    }
49
50    /// Number of listeners registered for `E` (tests / diagnostics).
51    pub fn listener_count<E: Event>(&self) -> usize {
52        self.inner
53            .lock()
54            .expect("EventBus")
55            .get(&TypeId::of::<E>())
56            .map(|v| v.len())
57            .unwrap_or(0)
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use std::sync::atomic::{AtomicUsize, Ordering};
65
66    struct Ping;
67    impl Event for Ping {
68        fn name(&self) -> &'static str {
69            "ping"
70        }
71    }
72
73    #[test]
74    fn order_and_multiple() {
75        let bus = EventBus::new();
76        let log = Arc::new(Mutex::new(Vec::new()));
77        let a = Arc::clone(&log);
78        let b = Arc::clone(&log);
79        bus.listen::<Ping, _>(move |_| a.lock().unwrap().push(1));
80        bus.listen::<Ping, _>(move |_| b.lock().unwrap().push(2));
81        assert_eq!(bus.listener_count::<Ping>(), 2);
82        bus.dispatch(Ping);
83        assert_eq!(*log.lock().unwrap(), vec![1, 2]);
84    }
85
86    #[test]
87    fn no_listeners_ok() {
88        let bus = EventBus::new();
89        let n = AtomicUsize::new(0);
90        bus.dispatch(Ping);
91        assert_eq!(n.load(Ordering::SeqCst), 0);
92    }
93}