use std::collections::HashMap;
use std::any::{Any, TypeId};
pub struct EventBus {
handlers: HashMap<TypeId, Vec<Box<dyn Fn(&dyn Any) + Send + Sync>>>,
}
impl EventBus {
pub fn new() -> Self {
Self {
handlers: HashMap::new(),
}
}
pub fn subscribe<T, F>(&mut self, handler: F)
where
T: 'static,
F: Fn(&T) + Send + Sync + 'static,
{
let type_id = TypeId::of::<T>();
let boxed_handler: Box<dyn Fn(&dyn Any) + Send + Sync> = Box::new(move |event| {
if let Some(typed_event) = event.downcast_ref::<T>() {
handler(typed_event);
}
});
self.handlers
.entry(type_id)
.or_insert_with(Vec::new)
.push(boxed_handler);
}
pub fn publish<T: 'static>(&self, event: &T) {
let type_id = TypeId::of::<T>();
if let Some(handlers) = self.handlers.get(&type_id) {
for handler in handlers {
handler(event);
}
}
}
pub fn subscriber_count<T: 'static>(&self) -> usize {
let type_id = TypeId::of::<T>();
self.handlers.get(&type_id).map(|h| h.len()).unwrap_or(0)
}
pub fn clear(&mut self) {
self.handlers.clear();
}
}
impl Default for EventBus {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct FileStatusChanged {
pub path: String,
pub staged: bool,
}
#[derive(Debug, Clone)]
pub struct CommitsChanged {
pub count: usize,
}
#[derive(Debug, Clone)]
pub struct BranchesChanged {
pub current: String,
pub count: usize,
}
#[derive(Debug, Clone)]
pub struct FocusChanged {
pub from: String,
pub to: String,
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
#[test]
fn test_subscribe_and_publish() {
let mut bus = EventBus::new();
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = counter.clone();
bus.subscribe::<FileStatusChanged, _>(move |_event| {
counter_clone.fetch_add(1, Ordering::SeqCst);
});
bus.publish(&FileStatusChanged {
path: "test.rs".to_string(),
staged: true,
});
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[test]
fn test_subscriber_count() {
let mut bus = EventBus::new();
bus.subscribe::<FileStatusChanged, _>(|_| {});
bus.subscribe::<FileStatusChanged, _>(|_| {});
assert_eq!(bus.subscriber_count::<FileStatusChanged>(), 2);
assert_eq!(bus.subscriber_count::<CommitsChanged>(), 0);
}
}