use std::any::{Any, TypeId};
use std::sync::{Arc, Mutex, OnceLock};
use dashmap::DashMap;
use tokio::sync::broadcast;
const DEFAULT_CAPACITY: usize = 64;
pub trait Event: Clone + Send + Sync + 'static {}
#[derive(Clone)]
pub struct ProcessEventBus {
channels: Arc<DashMap<TypeId, Box<dyn Any + Send + Sync>>>,
capacity: usize,
}
type EventFilter<E> = Box<dyn Fn(&E) -> bool + Send + Sync>;
struct FilteredSender<E: Event> {
accepts: EventFilter<E>,
sender: broadcast::Sender<E>,
}
struct Channel<E: Event> {
sender: broadcast::Sender<E>,
filtered: OnceLock<Mutex<Vec<FilteredSender<E>>>>,
}
impl<E: Event> Channel<E> {
fn new(capacity: usize) -> Self {
let (sender, _) = broadcast::channel(capacity);
Self { sender, filtered: OnceLock::new() }
}
fn emit(
&self,
event: E,
) {
if let Some(filtered) = self.filtered.get() {
let mut subscribers =
filtered.lock().expect("event filters lock poisoned");
subscribers.retain(|subscriber| {
if subscriber.sender.receiver_count() == 0 {
return false;
}
if (subscriber.accepts)(&event) {
let _ = subscriber.sender.send(event.clone());
}
true
});
}
let _ = self.sender.send(event);
}
}
impl ProcessEventBus {
pub fn new() -> Self {
Self::with_capacity(DEFAULT_CAPACITY)
}
pub fn with_capacity(capacity: usize) -> Self {
Self { channels: Arc::new(DashMap::new()), capacity }
}
pub fn emit<E: Event>(
&self,
event: E,
) {
let key = TypeId::of::<E>();
tracing::debug!(
event_type = std::any::type_name::<E>(),
"process event emitted"
);
if let Some(entry) = self.channels.get(&key)
&& let Some(channel) = entry.downcast_ref::<Arc<Channel<E>>>()
{
channel.emit(event);
}
}
pub fn subscribe<E: Event>(&self) -> broadcast::Receiver<E> {
self.channel::<E>().sender.subscribe()
}
pub fn subscribe_filtered<E: Event>(
&self,
accepts: impl Fn(&E) -> bool + Send + Sync + 'static,
) -> broadcast::Receiver<E> {
self.subscribe_filtered_with_capacity(self.capacity, accepts)
}
pub fn subscribe_filtered_with_capacity<E: Event>(
&self,
capacity: usize,
accepts: impl Fn(&E) -> bool + Send + Sync + 'static,
) -> broadcast::Receiver<E> {
let (sender, receiver) = broadcast::channel(capacity);
let channel = self.channel::<E>();
let mut subscribers = channel
.filtered
.get_or_init(Mutex::default)
.lock()
.expect("event filters lock poisoned");
subscribers
.retain(|subscriber| subscriber.sender.receiver_count() != 0);
subscribers.push(FilteredSender { accepts: Box::new(accepts), sender });
receiver
}
fn channel<E: Event>(&self) -> Arc<Channel<E>> {
let entry =
self.channels.entry(TypeId::of::<E>()).or_insert_with(|| {
Box::new(Arc::new(Channel::<E>::new(self.capacity)))
as Box<dyn Any + Send + Sync>
});
Arc::clone(
entry
.downcast_ref::<Arc<Channel<E>>>()
.expect("channel for TypeId is always Channel<E>"),
)
}
pub fn registered_type_count(&self) -> usize {
self.channels.len()
}
}
impl Default for ProcessEventBus {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct ShutdownInitiated;
impl Event for ShutdownInitiated {}
#[derive(Clone, Debug)]
pub struct ReloadRequired;
impl Event for ReloadRequired {}