use std::marker::PhantomData;
use std::sync::Arc;
use rskit_errors::{AppError, AppResult, ErrorCode};
use tokio::sync::broadcast;
use crate::Event;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EventBusConfig {
pub capacity: usize,
}
impl Default for EventBusConfig {
fn default() -> Self {
Self { capacity: 1024 }
}
}
pub struct EventBus<E: Event> {
sender: broadcast::Sender<Arc<E>>,
}
impl<E: Event> Clone for EventBus<E> {
fn clone(&self) -> Self {
Self {
sender: self.sender.clone(),
}
}
}
impl<E: Event> EventBus<E> {
#[must_use]
pub fn new(config: EventBusConfig) -> Self {
let capacity = config.capacity.max(1);
let (sender, _) = broadcast::channel(capacity);
Self { sender }
}
pub fn publish(&self, event: E) -> AppResult<usize> {
self.sender.send(Arc::new(event)).or(Ok(0))
}
#[must_use]
pub fn subscribe(&self) -> Subscriber<E> {
Subscriber {
receiver: self.sender.subscribe(),
}
}
}
pub struct EventRegistry<E: Event> {
bus: EventBus<E>,
}
impl<E: Event> EventRegistry<E> {
#[must_use]
pub fn new(config: EventBusConfig) -> Self {
Self {
bus: EventBus::new(config),
}
}
#[must_use]
pub fn publisher(&self) -> EventBus<E> {
self.bus.clone()
}
#[must_use]
pub fn register_subscriber(&self) -> Subscriber<E> {
self.bus.subscribe()
}
}
impl<E: Event> Default for EventRegistry<E> {
fn default() -> Self {
Self::new(EventBusConfig::default())
}
}
pub struct Subscriber<E: Event> {
receiver: broadcast::Receiver<Arc<E>>,
}
impl<E: Event> Subscriber<E> {
pub async fn recv(&mut self) -> AppResult<Arc<E>> {
self.receiver.recv().await.map_err(|error| match error {
broadcast::error::RecvError::Closed => {
AppError::new(ErrorCode::Cancelled, "event bus publisher closed")
}
broadcast::error::RecvError::Lagged(skipped) => AppError::new(
ErrorCode::RateLimited,
format!("event subscriber lagged by {skipped} messages"),
),
})
}
}
impl<E: Event> std::fmt::Debug for EventBus<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EventBus")
.field("event_type", &std::any::type_name::<E>())
.field("receiver_count", &self.sender.receiver_count())
.finish()
}
}
impl<E: Event> std::fmt::Debug for EventRegistry<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EventRegistry")
.field("event_type", &std::any::type_name::<E>())
.finish_non_exhaustive()
}
}
impl<E: Event> std::fmt::Debug for Subscriber<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let _phantom = PhantomData::<E>;
f.debug_struct("Subscriber")
.field("event_type", &std::any::type_name::<E>())
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use super::{EventBus, EventBusConfig, EventRegistry};
use crate::{Event, EventType};
#[derive(Debug)]
struct Ping(u32);
impl Event for Ping {
fn event_type(&self) -> EventType {
EventType::new("ping")
}
}
#[tokio::test]
async fn bus_publishes_to_registered_subscriber() {
let bus = EventBus::<Ping>::new(EventBusConfig { capacity: 4 });
let mut subscriber = bus.subscribe();
assert_eq!(bus.publish(Ping(7)).expect("publish"), 1);
let event = subscriber.recv().await.expect("receive");
assert_eq!(event.0, 7);
}
#[tokio::test]
async fn publish_without_subscribers_succeeds() {
let bus = EventBus::<Ping>::new(EventBusConfig { capacity: 4 });
assert_eq!(bus.publish(Ping(1)).expect("publish"), 0);
}
#[tokio::test]
async fn subscriber_reports_lag() {
let bus = EventBus::<Ping>::new(EventBusConfig { capacity: 1 });
let mut subscriber = bus.subscribe();
let _ = bus.publish(Ping(1)).expect("first");
let _ = bus.publish(Ping(2)).expect("second");
let err = subscriber.recv().await.expect_err("lag should be reported");
assert_eq!(err.code(), rskit_errors::ErrorCode::RateLimited);
}
#[test]
fn event_registry_registers_subscribers_explicitly() {
let registry = EventRegistry::<Ping>::new(EventBusConfig { capacity: 4 });
let _subscriber = registry.register_subscriber();
let publisher = registry.publisher();
assert_eq!(publisher.publish(Ping(1)).expect("publish"), 1);
}
}