use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use rainier_support::{BoxedFuture, Error, Result};
use crate::event::{Event, EventSubscriber, Listener, WildcardListener};
type StoredHandler =
Arc<dyn Fn(Arc<dyn Any + Send + Sync>) -> BoxedFuture<Result<()>> + Send + Sync>;
#[derive(Clone)]
struct Registration {
handler: StoredHandler,
priority: i32,
sequence: usize,
}
type WildcardRegistration = (Arc<dyn WildcardListener>, i32, usize);
struct Recorded {
type_id: TypeId,
name: &'static str,
payload: Arc<dyn Any + Send + Sync>,
}
#[derive(Default)]
pub struct Dispatcher {
listeners: RwLock<HashMap<TypeId, Vec<Registration>>>,
wildcards: RwLock<Vec<WildcardRegistration>>,
sequence: AtomicUsize,
recorder: Option<Mutex<Vec<Recorded>>>,
}
impl Dispatcher {
pub fn new() -> Self {
Self::default()
}
pub fn fake() -> Self {
Self { recorder: Some(Mutex::new(Vec::new())), ..Self::default() }
}
pub fn is_faking(&self) -> bool {
self.recorder.is_some()
}
pub fn listen<E, L>(&self, listener: L)
where
E: Event,
L: Listener<E>,
{
self.listen_with_priority(listener, 0);
}
pub fn listen_with_priority<E, L>(&self, listener: L, priority: i32)
where
E: Event,
L: Listener<E>,
{
let listener = Arc::new(listener);
let handler: StoredHandler = Arc::new(move |erased| {
let listener = Arc::clone(&listener);
match erased.downcast::<E>() {
Ok(event) => listener.handle(event),
Err(_) => {
let name = std::any::type_name::<E>();
Box::pin(async move {
Err(Error::internal(format!(
"listener for `{name}` received a different event type"
)))
})
}
}
});
let registration = Registration {
handler,
priority,
sequence: self.sequence.fetch_add(1, Ordering::SeqCst),
};
self.listeners
.write()
.expect("listeners lock poisoned")
.entry(TypeId::of::<E>())
.or_default()
.push(registration);
}
pub fn listen_sync<E, F>(&self, listener: F)
where
E: Event,
F: Fn(Arc<E>) -> Result<()> + Send + Sync + 'static,
{
self.listen(move |event: Arc<E>| {
let outcome = listener(event);
async move { outcome }
});
}
pub fn listen_any<L: WildcardListener>(&self, listener: L) {
self.listen_any_with_priority(listener, 0);
}
pub fn listen_any_with_priority<L: WildcardListener>(&self, listener: L, priority: i32) {
let sequence = self.sequence.fetch_add(1, Ordering::SeqCst);
self.wildcards.write().expect("wildcards lock poisoned").push((
Arc::new(listener),
priority,
sequence,
));
}
pub fn subscribe(&self, subscriber: &impl EventSubscriber) {
subscriber.subscribe(self);
}
pub fn has_listeners<E: Event>(&self) -> bool {
self.listeners
.read()
.expect("listeners lock poisoned")
.get(&TypeId::of::<E>())
.is_some_and(|l| !l.is_empty())
}
pub fn forget<E: Event>(&self) {
self.listeners.write().expect("listeners lock poisoned").remove(&TypeId::of::<E>());
}
pub fn forget_all(&self) {
self.listeners.write().expect("listeners lock poisoned").clear();
self.wildcards.write().expect("wildcards lock poisoned").clear();
}
pub async fn dispatch<E: Event>(&self, event: E) -> Result<()> {
self.dispatch_as(E::event_name(), event).await
}
pub async fn dispatch_as<E: Event>(&self, name: &'static str, event: E) -> Result<()> {
let payload: Arc<dyn Any + Send + Sync> = Arc::new(event);
if let Some(recorder) = &self.recorder {
recorder.lock().expect("recorder lock poisoned").push(Recorded {
type_id: TypeId::of::<E>(),
name,
payload,
});
return Ok(());
}
for handler in self.handlers_for::<E>() {
handler(Arc::clone(&payload)).await?;
}
for wildcard in self.wildcard_handlers() {
wildcard.handle(name, Arc::clone(&payload)).await?;
}
Ok(())
}
pub async fn dispatch_quietly<E: Event>(&self, event: E) -> usize {
let name = E::event_name();
let payload: Arc<dyn Any + Send + Sync> = Arc::new(event);
if let Some(recorder) = &self.recorder {
recorder.lock().expect("recorder lock poisoned").push(Recorded {
type_id: TypeId::of::<E>(),
name,
payload,
});
return 0;
}
let mut failures = 0;
for handler in self.handlers_for::<E>() {
if let Err(e) = handler(Arc::clone(&payload)).await {
tracing::error!(event = name, error = %e, "event listener failed");
failures += 1;
}
}
for wildcard in self.wildcard_handlers() {
if let Err(e) = wildcard.handle(name, Arc::clone(&payload)).await {
tracing::error!(event = name, error = %e, "wildcard event listener failed");
failures += 1;
}
}
failures
}
fn handlers_for<E: Event>(&self) -> Vec<StoredHandler> {
let listeners = self.listeners.read().expect("listeners lock poisoned");
let Some(registrations) = listeners.get(&TypeId::of::<E>()) else {
return Vec::new();
};
let mut sorted = registrations.clone();
drop(listeners);
sorted.sort_by(|a, b| b.priority.cmp(&a.priority).then(a.sequence.cmp(&b.sequence)));
sorted.into_iter().map(|r| r.handler).collect()
}
fn wildcard_handlers(&self) -> Vec<Arc<dyn WildcardListener>> {
let wildcards = self.wildcards.read().expect("wildcards lock poisoned");
let mut sorted = wildcards.clone();
drop(wildcards);
sorted.sort_by(|a, b| b.1.cmp(&a.1).then(a.2.cmp(&b.2)));
sorted.into_iter().map(|(listener, _, _)| listener).collect()
}
pub fn dispatched<E: Event>(&self) -> Vec<Arc<E>> {
let Some(recorder) = &self.recorder else {
return Vec::new();
};
recorder
.lock()
.expect("recorder lock poisoned")
.iter()
.filter(|r| r.type_id == TypeId::of::<E>())
.filter_map(|r| Arc::clone(&r.payload).downcast::<E>().ok())
.collect()
}
pub fn dispatched_names(&self) -> Vec<&'static str> {
let Some(recorder) = &self.recorder else {
return Vec::new();
};
recorder.lock().expect("recorder lock poisoned").iter().map(|r| r.name).collect()
}
pub fn assert_dispatched<E: Event>(&self) {
self.require_faking("assert_dispatched");
assert!(
!self.dispatched::<E>().is_empty(),
"expected `{}` to have been dispatched, but it was not. Dispatched: {:?}",
E::event_name(),
self.dispatched_names()
);
}
pub fn assert_dispatched_times<E: Event>(&self, times: usize) {
self.require_faking("assert_dispatched_times");
let actual = self.dispatched::<E>().len();
assert_eq!(
actual,
times,
"expected `{}` to have been dispatched {times} time(s), but it was dispatched {actual}",
E::event_name(),
);
}
pub fn assert_not_dispatched<E: Event>(&self) {
self.require_faking("assert_not_dispatched");
assert!(
self.dispatched::<E>().is_empty(),
"expected `{}` not to have been dispatched, but it was",
E::event_name()
);
}
fn require_faking(&self, method: &str) {
assert!(
self.is_faking(),
"`{method}` needs a faking dispatcher — build it with `Dispatcher::fake()`, \
otherwise nothing is recorded and the assertion is meaningless"
);
}
}
impl std::fmt::Debug for Dispatcher {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Dispatcher")
.field("event_types", &self.listeners.read().map(|l| l.len()).unwrap_or(0))
.field("wildcards", &self.wildcards.read().map(|w| w.len()).unwrap_or(0))
.field("faking", &self.is_faking())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct Shipped(u64);
#[derive(Debug)]
struct Cancelled;
type Log = Arc<Mutex<Vec<String>>>;
fn recorder() -> (Log, Log) {
let log: Log = Arc::new(Mutex::new(Vec::new()));
(Arc::clone(&log), log)
}
#[tokio::test]
async fn delivers_to_listeners_of_the_matching_type() {
let (log, out) = recorder();
let events = Dispatcher::new();
events.listen(move |event: Arc<Shipped>| {
let log = Arc::clone(&log);
async move {
log.lock().unwrap().push(format!("shipped:{}", event.0));
Ok(())
}
});
events.dispatch(Shipped(7)).await.unwrap();
events.dispatch(Cancelled).await.unwrap();
assert_eq!(*out.lock().unwrap(), vec!["shipped:7"]);
}
#[tokio::test]
async fn several_listeners_run_in_registration_order() {
let (log, out) = recorder();
let events = Dispatcher::new();
for tag in ["a", "b", "c"] {
let log = Arc::clone(&log);
events.listen(move |_: Arc<Shipped>| {
let log = Arc::clone(&log);
async move {
log.lock().unwrap().push(tag.to_string());
Ok(())
}
});
}
events.dispatch(Shipped(1)).await.unwrap();
assert_eq!(*out.lock().unwrap(), vec!["a", "b", "c"]);
}
#[tokio::test]
async fn higher_priority_runs_first() {
let (log, out) = recorder();
let events = Dispatcher::new();
for (tag, priority) in [("low", -10), ("normal", 0), ("high", 10)] {
let log = Arc::clone(&log);
events.listen_with_priority(
move |_: Arc<Shipped>| {
let log = Arc::clone(&log);
async move {
log.lock().unwrap().push(tag.to_string());
Ok(())
}
},
priority,
);
}
events.dispatch(Shipped(1)).await.unwrap();
assert_eq!(*out.lock().unwrap(), vec!["high", "normal", "low"]);
}
#[tokio::test]
async fn an_erroring_listener_halts_the_chain() {
let (log, out) = recorder();
let events = Dispatcher::new();
events.listen(|_: Arc<Shipped>| async { Err(Error::internal("veto")) });
events.listen(move |_: Arc<Shipped>| {
let log = Arc::clone(&log);
async move {
log.lock().unwrap().push("ran".into());
Ok(())
}
});
let err = events.dispatch(Shipped(1)).await.unwrap_err();
assert_eq!(err.message(), "veto");
assert!(out.lock().unwrap().is_empty(), "the second listener must not have run");
}
#[tokio::test]
async fn dispatch_quietly_runs_everything_and_counts_failures() {
let (log, out) = recorder();
let events = Dispatcher::new();
events.listen(|_: Arc<Shipped>| async { Err(Error::internal("down")) });
events.listen(move |_: Arc<Shipped>| {
let log = Arc::clone(&log);
async move {
log.lock().unwrap().push("ran".into());
Ok(())
}
});
assert_eq!(events.dispatch_quietly(Shipped(1)).await, 1);
assert_eq!(*out.lock().unwrap(), vec!["ran"]);
}
#[tokio::test]
async fn sync_listeners_need_no_async_block() {
let (log, out) = recorder();
let events = Dispatcher::new();
events.listen_sync(move |event: Arc<Shipped>| {
log.lock().unwrap().push(event.0.to_string());
Ok(())
});
events.dispatch(Shipped(3)).await.unwrap();
assert_eq!(*out.lock().unwrap(), vec!["3"]);
}
#[tokio::test]
async fn wildcard_listeners_see_every_event_by_name() {
let (log, out) = recorder();
let events = Dispatcher::new();
events.listen_any(move |name: &'static str, _: Arc<dyn Any + Send + Sync>| {
let log = Arc::clone(&log);
async move {
log.lock().unwrap().push(name.rsplit("::").next().unwrap().to_string());
Ok(())
}
});
events.dispatch(Shipped(1)).await.unwrap();
events.dispatch(Cancelled).await.unwrap();
assert_eq!(*out.lock().unwrap(), vec!["Shipped", "Cancelled"]);
}
#[tokio::test]
async fn a_wildcard_listener_can_recover_the_concrete_event() {
let (log, out) = recorder();
let events = Dispatcher::new();
events.listen_any(move |_: &'static str, payload: Arc<dyn Any + Send + Sync>| {
let log = Arc::clone(&log);
async move {
if let Ok(shipped) = payload.downcast::<Shipped>() {
log.lock().unwrap().push(shipped.0.to_string());
}
Ok(())
}
});
events.dispatch(Shipped(9)).await.unwrap();
events.dispatch(Cancelled).await.unwrap();
assert_eq!(*out.lock().unwrap(), vec!["9"]);
}
#[tokio::test]
async fn dispatch_as_overrides_the_name_wildcards_see() {
let (log, out) = recorder();
let events = Dispatcher::new();
events.listen_any(move |name: &'static str, _: Arc<dyn Any + Send + Sync>| {
let log = Arc::clone(&log);
async move {
log.lock().unwrap().push(name.to_string());
Ok(())
}
});
events.dispatch_as("order.shipped", Shipped(1)).await.unwrap();
assert_eq!(*out.lock().unwrap(), vec!["order.shipped"]);
}
#[tokio::test]
async fn subscribers_register_a_group_of_listeners() {
struct Orders;
impl EventSubscriber for Orders {
fn subscribe(&self, events: &Dispatcher) {
events.listen(|_: Arc<Shipped>| async { Ok(()) });
events.listen(|_: Arc<Cancelled>| async { Ok(()) });
}
}
let events = Dispatcher::new();
events.subscribe(&Orders);
assert!(events.has_listeners::<Shipped>());
assert!(events.has_listeners::<Cancelled>());
}
#[tokio::test]
async fn forget_removes_listeners() {
let events = Dispatcher::new();
events.listen(|_: Arc<Shipped>| async { Ok(()) });
assert!(events.has_listeners::<Shipped>());
events.forget::<Shipped>();
assert!(!events.has_listeners::<Shipped>());
}
#[tokio::test]
async fn a_fake_records_instead_of_delivering() {
let (log, out) = recorder();
let events = Dispatcher::fake();
events.listen(move |_: Arc<Shipped>| {
let log = Arc::clone(&log);
async move {
log.lock().unwrap().push("should not run".into());
Ok(())
}
});
events.dispatch(Shipped(1)).await.unwrap();
events.dispatch(Shipped(2)).await.unwrap();
assert!(out.lock().unwrap().is_empty(), "listeners must not run while faking");
events.assert_dispatched::<Shipped>();
events.assert_dispatched_times::<Shipped>(2);
events.assert_not_dispatched::<Cancelled>();
let recorded = events.dispatched::<Shipped>();
assert_eq!(recorded.iter().map(|s| s.0).collect::<Vec<_>>(), vec![1, 2]);
}
#[tokio::test]
#[should_panic(expected = "needs a faking dispatcher")]
async fn assertions_refuse_to_pass_vacuously_on_a_real_dispatcher() {
let events = Dispatcher::new();
events.dispatch(Shipped(1)).await.unwrap();
events.assert_not_dispatched::<Shipped>();
}
#[tokio::test]
async fn dispatching_with_no_listeners_is_fine() {
let events = Dispatcher::new();
events.dispatch(Shipped(1)).await.unwrap();
assert_eq!(events.dispatch_quietly(Shipped(1)).await, 0);
}
}