use std::any::TypeId;
use std::collections::HashMap;
use std::sync::Arc;
pub trait Event: Clone + Send + Sync + 'static {}
pub trait Listener<E: Event>: Send + Sync {
fn handle(&self, event: &E);
}
pub struct EventDispatcher {
listeners: HashMap<TypeId, Vec<Arc<ErasedListener>>>,
}
type ErasedHandler = dyn Fn(&(dyn std::any::Any + Send + Sync)) + Send + Sync;
struct ErasedListener {
handle: Box<ErasedHandler>,
}
impl ErasedListener {
fn new<E: Event, L: Listener<E> + 'static>(listener: Arc<L>) -> Self {
let l = listener;
ErasedListener {
handle: Box::new(move |event: &(dyn std::any::Any + Send + Sync)| {
if let Some(e) = event.downcast_ref::<E>() {
l.handle(e);
}
}),
}
}
fn handle_any(&self, event: &(dyn std::any::Any + Send + Sync)) {
(self.handle)(event);
}
}
impl EventDispatcher {
pub fn new() -> Self {
Self {
listeners: HashMap::new(),
}
}
pub fn listen<E: Event, L: Listener<E> + 'static>(&mut self, _dummy: E, listener: Arc<L>) {
self.listeners
.entry(TypeId::of::<E>())
.or_default()
.push(Arc::new(ErasedListener::new::<E, L>(listener)));
}
pub fn dispatch<E: Event>(&self, event: &E) {
if let Some(listeners) = self.listeners.get(&TypeId::of::<E>()) {
for listener in listeners {
listener.handle_any(event);
}
}
}
pub fn forget<E: Event>(&mut self) {
self.listeners.remove(&TypeId::of::<E>());
}
pub fn flush(&mut self) {
self.listeners.clear();
}
pub fn len(&self) -> usize {
self.listeners.len()
}
pub fn is_empty(&self) -> bool {
self.listeners.is_empty()
}
}
impl Default for EventDispatcher {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Clone)]
struct OrderPlaced {
order_id: u32,
}
impl Event for OrderPlaced {}
struct OrderLogger {
counter: Arc<AtomicUsize>,
}
impl Listener<OrderPlaced> for OrderLogger {
fn handle(&self, e: &OrderPlaced) {
self.counter
.fetch_add(e.order_id as usize, Ordering::SeqCst);
}
}
#[test]
fn test_dispatch_to_single_listener() {
let counter = Arc::new(AtomicUsize::new(0));
let logger = OrderLogger {
counter: counter.clone(),
};
let mut d = EventDispatcher::new();
d.listen(OrderPlaced { order_id: 0 }, Arc::new(logger));
d.dispatch(&OrderPlaced { order_id: 42 });
assert_eq!(counter.load(Ordering::SeqCst), 42);
}
#[test]
fn test_multiple_listeners() {
let counter = Arc::new(AtomicUsize::new(0));
let mut d = EventDispatcher::new();
d.listen(
OrderPlaced { order_id: 0 },
Arc::new(OrderLogger {
counter: counter.clone(),
}),
);
d.listen(
OrderPlaced { order_id: 0 },
Arc::new(OrderLogger {
counter: counter.clone(),
}),
);
d.dispatch(&OrderPlaced { order_id: 10 });
assert_eq!(counter.load(Ordering::SeqCst), 20);
}
#[test]
fn test_forget_removes_listeners() {
let counter = Arc::new(AtomicUsize::new(0));
let mut d = EventDispatcher::new();
d.listen(
OrderPlaced { order_id: 0 },
Arc::new(OrderLogger {
counter: counter.clone(),
}),
);
d.forget::<OrderPlaced>();
d.dispatch(&OrderPlaced { order_id: 100 });
assert_eq!(counter.load(Ordering::SeqCst), 0);
}
}