use std::{
ops::Deref,
sync::{Mutex, OnceLock},
};
use crate::resources::{Resource, ResourceConsumer};
pub trait Event: Clone {}
pub struct EventBus<E: Event> {
listeners: Mutex<Vec<Box<dyn Fn(E) -> anyhow::Result<()> + Send>>>,
}
impl<E: Event> Default for EventBus<E> {
fn default() -> Self {
Self {
listeners: Mutex::new(Vec::with_capacity(4)),
}
}
}
impl<E: Event> EventBus<E> {
pub fn subscribe<F: Fn(E) -> anyhow::Result<()> + Send + 'static>(&self, listener: F) {
let mut listeners = self.listeners.lock().unwrap();
listeners.push(Box::new(listener));
}
pub fn publish(&self, event: E) {
for listener in self.listeners.lock().unwrap().deref() {
if let Err(e) = listener(event.clone()) {
log::error!("Error in event handler: {e:?}")
}
}
}
pub fn publish_lazy(&self, create_event: impl FnOnce() -> E) {
let listeners = self.listeners.lock().unwrap();
match &listeners[..] {
[] => (),
[listener] => {
if let Err(e) = listener(create_event()) {
log::error!("Error in event handler: {e:?}")
}
}
listeners => {
let event = create_event();
for listener in listeners {
if let Err(e) = listener(event.clone()) {
log::error!("Error in event handler: {e:?}")
}
}
}
}
}
}
#[derive(Default)]
struct EventBuses {
start_consumer_measurement: EventBus<StartConsumerMeasurement>,
start_resource_measurement: EventBus<StartResourceMeasurement>,
}
static GLOBAL_EVENT_BUSES: OnceLock<EventBuses> = OnceLock::new();
pub fn start_consumer_measurement() -> &'static EventBus<StartConsumerMeasurement> {
&GLOBAL_EVENT_BUSES
.get_or_init(EventBuses::default)
.start_consumer_measurement
}
pub fn start_resource_measurement() -> &'static EventBus<StartResourceMeasurement> {
&GLOBAL_EVENT_BUSES
.get_or_init(EventBuses::default)
.start_resource_measurement
}
#[derive(Clone)]
pub struct StartConsumerMeasurement(pub Vec<ResourceConsumer>);
#[derive(Clone)]
pub struct StartResourceMeasurement(pub Vec<Resource>);
impl Event for StartConsumerMeasurement {}
impl Event for StartResourceMeasurement {}
#[cfg(test)]
mod tests {
use std::sync::{
atomic::{AtomicU32, Ordering},
Arc,
};
use super::{Event, EventBus};
#[derive(Clone)]
struct TestEvent(u32);
impl Event for TestEvent {}
#[test]
fn test() {
let bus: EventBus<TestEvent> = EventBus::default();
let event_count = Arc::new(AtomicU32::new(0));
let cloned_count = event_count.clone();
bus.publish(TestEvent(123));
assert_eq!(
0,
event_count.load(Ordering::SeqCst),
"count should remain 0 because there's no listener yet"
);
bus.subscribe(move |event| {
cloned_count.fetch_add(event.0, Ordering::SeqCst);
Ok(())
});
bus.publish(TestEvent(1));
assert_eq!(1, event_count.load(Ordering::SeqCst));
bus.publish(TestEvent(10));
assert_eq!(11, event_count.load(Ordering::SeqCst));
}
}