1#[cfg(test)]
29mod tests;
30
31use std::any::{Any, TypeId};
32use std::collections::HashMap;
33use std::panic::{catch_unwind, AssertUnwindSafe};
34use std::sync::atomic::{AtomicU64, Ordering};
35use std::sync::{Arc, RwLock};
36
37type Subscriber = Arc<dyn Fn(&dyn Any) + Send + Sync>;
38type Subscribers = HashMap<TypeId, Vec<(SubscriptionId, Subscriber)>>;
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43pub struct SubscriptionId(u64);
44
45#[derive(Clone, Default)]
47pub struct EventBus {
48 subscribers: Arc<RwLock<Subscribers>>,
49 next_id: Arc<AtomicU64>,
50}
51
52impl EventBus {
53 pub fn new() -> Self {
54 EventBus::default()
55 }
56
57 pub fn subscribe<E, F>(&self, handler: F) -> SubscriptionId
59 where
60 E: Any,
61 F: Fn(&E) + Send + Sync + 'static,
62 {
63 let id = SubscriptionId(self.next_id.fetch_add(1, Ordering::Relaxed));
64 let subscriber: Subscriber = Arc::new(move |event: &dyn Any| {
65 if let Some(event) = event.downcast_ref::<E>() {
66 handler(event);
67 }
68 });
69 self.subscribers.write().unwrap_or_else(|e| e.into_inner()).entry(TypeId::of::<E>()).or_default().push((id, subscriber));
70 id
71 }
72
73 pub fn unsubscribe(&self, id: SubscriptionId) -> bool {
75 let mut subscribers = self.subscribers.write().unwrap_or_else(|e| e.into_inner());
76 for list in subscribers.values_mut() {
77 if let Some(position) = list.iter().position(|(i, _)| *i == id) {
78 list.remove(position);
79 return true;
80 }
81 }
82 false
83 }
84
85 pub fn publish<E: Any>(&self, event: &E) -> usize {
88 let subscribers: Vec<Subscriber> = match self.subscribers.read().unwrap_or_else(|e| e.into_inner()).get(&TypeId::of::<E>()) {
90 Some(list) => list.iter().map(|(_, s)| Arc::clone(s)).collect(),
91 None => return 0,
92 };
93 let mut completed = 0;
94 for subscriber in subscribers {
95 match catch_unwind(AssertUnwindSafe(|| subscriber(event))) {
96 Ok(()) => completed += 1,
97 Err(_) => eprintln!("[events] a subscriber to {} panicked", std::any::type_name::<E>()),
98 }
99 }
100 completed
101 }
102
103 pub fn subscribers<E: Any>(&self) -> usize {
105 self.subscribers.read().unwrap_or_else(|e| e.into_inner()).get(&TypeId::of::<E>()).map_or(0, Vec::len)
106 }
107
108 pub fn forward_to_mcp<E, F>(&self, server: crate::mcp::McpServer, method: &str, params: F) -> SubscriptionId
112 where
113 E: Any,
114 F: Fn(&E) -> String + Send + Sync + 'static,
115 {
116 let method = method.to_string();
117 self.subscribe(move |event: &E| server.notify(&method, Some(¶ms(event))))
118 }
119
120 #[cfg(not(target_arch = "wasm32"))]
121 pub fn forward_to_channel<E, T, M>(&self, hub: crate::channels::Hub, topic: T, message: M) -> SubscriptionId
124 where
125 E: Any,
126 T: Fn(&E) -> String + Send + Sync + 'static,
127 M: Fn(&E) -> String + Send + Sync + 'static,
128 {
129 self.subscribe(move |event: &E| {
130 hub.publish(&topic(event), &message(event));
131 })
132 }
133}