use crate::{
app::{App, AppBuilder},
config::env::Env,
error::Result,
plugin::ComponentRegistry,
};
use async_trait::async_trait;
use dashmap::DashMap;
use std::{
any::{Any, TypeId},
future::Future,
net::SocketAddr,
path::PathBuf,
sync::Arc,
};
#[async_trait]
pub trait BuilderEventListener: Send + Sync {
async fn on_event(
&self,
event: Arc<dyn Any + Send + Sync>,
app: &mut AppBuilder,
) -> Result<()>;
}
struct TypedBuilderListener<E, F> {
listener: F,
_marker: std::marker::PhantomData<fn(E)>,
}
#[async_trait]
impl<E, F, Fut> BuilderEventListener for TypedBuilderListener<E, F>
where
E: Event,
F: Fn(E, &mut AppBuilder) -> Fut + Send + Sync,
Fut: Future<Output = Result<()>> + Send,
{
async fn on_event(
&self,
event: Arc<dyn Any + Send + Sync>,
app: &mut AppBuilder,
) -> Result<()> {
let event = event
.downcast::<E>()
.expect("event listener received unexpected event type");
(self.listener)((*event).clone(), app).await
}
}
type BuilderListener = Arc<dyn BuilderEventListener>;
#[async_trait]
pub trait AppEventListener: Send + Sync {
async fn on_event(&self, event: Arc<dyn Any + Send + Sync>, app: &App) -> Result<()>;
}
struct TypedAppListener<E, F> {
listener: F,
_marker: std::marker::PhantomData<fn(E)>,
}
#[async_trait]
impl<E, F, Fut> AppEventListener for TypedAppListener<E, F>
where
E: Event,
F: Fn(E, &App) -> Fut + Send + Sync,
Fut: Future<Output = Result<()>> + Send,
{
async fn on_event(&self, event: Arc<dyn Any + Send + Sync>, app: &App) -> Result<()> {
let event = event
.downcast::<E>()
.expect("event listener received unexpected event type");
(self.listener)((*event).clone(), app).await
}
}
type AppListener = Arc<dyn AppEventListener>;
pub trait Event: Clone + Send + Sync + 'static {}
#[derive(Clone, Default)]
pub struct EventBus {
builder_listeners: Arc<DashMap<TypeId, Vec<BuilderListener>>>,
app_listeners: Arc<DashMap<TypeId, Vec<AppListener>>>,
}
impl EventBus {
pub fn listen_dyn<E>(&self, listener: BuilderListener)
where
E: Event,
{
self.builder_listeners
.entry(TypeId::of::<E>())
.or_default()
.push(listener);
}
pub fn listen_app_dyn<E>(&self, listener: AppListener)
where
E: Event,
{
self.app_listeners
.entry(TypeId::of::<E>())
.or_default()
.push(listener);
}
pub fn listen<E, F, Fut>(&self, listener: F)
where
E: Event,
F: Fn(E, &mut AppBuilder) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<()>> + Send,
{
self.builder_listeners
.entry(TypeId::of::<E>())
.or_default()
.push(Arc::new(TypedBuilderListener {
listener,
_marker: std::marker::PhantomData,
}));
}
pub fn listen_app<E, F, Fut>(&self, listener: F)
where
E: Event,
F: Fn(E, &App) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<()>> + Send,
{
self.app_listeners
.entry(TypeId::of::<E>())
.or_default()
.push(Arc::new(TypedAppListener {
listener,
_marker: std::marker::PhantomData,
}));
}
pub async fn publish_builder<E>(&self, event: E, app: &mut AppBuilder) -> Result<()>
where
E: Event,
{
let listeners = self
.builder_listeners
.get(&TypeId::of::<E>())
.map(|entry| entry.clone())
.unwrap_or_default();
let event = Arc::new(event) as Arc<dyn Any + Send + Sync>;
for listener in listeners {
listener.on_event(event.clone(), app).await?;
}
Ok(())
}
pub async fn publish_app<E>(&self, event: E, app: &App) -> Result<()>
where
E: Event,
{
let listeners = self
.app_listeners
.get(&TypeId::of::<E>())
.map(|entry| entry.clone())
.unwrap_or_default();
let event = Arc::new(event) as Arc<dyn Any + Send + Sync>;
for listener in listeners {
listener.on_event(event.clone(), app).await?;
}
Ok(())
}
}
#[async_trait]
pub trait BuilderEventPublisher {
async fn publish<E>(&mut self, event: E) -> Result<()>
where
E: Event;
}
#[async_trait]
pub trait EventPublisher: Sync {
async fn publish<E>(&self, event: E) -> Result<()>
where
E: Event;
}
pub trait EventSubscriber {
fn listen_dyn<E>(&self, listener: Arc<dyn BuilderEventListener>)
where
E: Event;
fn listen<E, F, Fut>(&self, listener: F)
where
E: Event,
F: Fn(E, &mut AppBuilder) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<()>> + Send;
}
pub trait AppEventSubscriber {
fn listen_app_dyn<E>(&self, listener: Arc<dyn AppEventListener>)
where
E: Event;
fn listen_app<E, F, Fut>(&self, listener: F)
where
E: Event,
F: Fn(E, &App) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<()>> + Send;
}
#[async_trait]
impl BuilderEventPublisher for AppBuilder {
async fn publish<E>(&mut self, event: E) -> Result<()>
where
E: Event,
{
let bus = self.get_expect_component::<EventBus>().clone();
bus.publish_builder(event, self).await
}
}
#[async_trait]
impl EventPublisher for App {
async fn publish<E>(&self, event: E) -> Result<()>
where
E: Event,
{
self.get_expect_component::<EventBus>()
.publish_app(event, self)
.await
}
}
impl EventSubscriber for AppBuilder {
fn listen_dyn<E>(&self, listener: Arc<dyn BuilderEventListener>)
where
E: Event,
{
self.get_expect_component::<EventBus>()
.listen_dyn::<E>(listener);
}
fn listen<E, F, Fut>(&self, listener: F)
where
E: Event,
F: Fn(E, &mut AppBuilder) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<()>> + Send,
{
self.get_expect_component::<EventBus>().listen(listener);
}
}
impl AppEventSubscriber for AppBuilder {
fn listen_app_dyn<E>(&self, listener: Arc<dyn AppEventListener>)
where
E: Event,
{
self.get_expect_component::<EventBus>()
.listen_app_dyn::<E>(listener);
}
fn listen_app<E, F, Fut>(&self, listener: F)
where
E: Event,
F: Fn(E, &App) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<()>> + Send,
{
self.get_expect_component::<EventBus>().listen_app(listener);
}
}
impl AppEventSubscriber for App {
fn listen_app_dyn<E>(&self, listener: Arc<dyn AppEventListener>)
where
E: Event,
{
self.get_expect_component::<EventBus>()
.listen_app_dyn::<E>(listener);
}
fn listen_app<E, F, Fut>(&self, listener: F)
where
E: Event,
F: Fn(E, &App) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<()>> + Send,
{
self.get_expect_component::<EventBus>().listen_app(listener);
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConfigSource {
File(PathBuf),
Inline,
}
#[derive(Debug, Clone)]
pub struct ConfigEvent {
pub env: Env,
pub source: ConfigSource,
}
impl Event for ConfigEvent {}
#[derive(Debug, Clone)]
pub struct PluginsBuiltEvent;
impl Event for PluginsBuiltEvent {}
#[derive(Debug, Clone)]
pub struct ServicesInjectedEvent;
impl Event for ServicesInjectedEvent {}
#[derive(Clone)]
pub struct AppBuiltEvent {
pub app: Arc<App>,
}
impl Event for AppBuiltEvent {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShutdownPhase {
BeforeHooks,
AfterHooks,
}
#[derive(Debug, Clone)]
pub struct ShutdownEvent {
pub phase: ShutdownPhase,
}
impl Event for ShutdownEvent {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ServerProtocol {
Http,
Grpc,
}
impl ServerProtocol {
pub fn as_str(self) -> &'static str {
match self {
Self::Http => "http",
Self::Grpc => "grpc",
}
}
}
#[derive(Debug, Clone)]
pub struct ServerStartedEvent {
pub addr: SocketAddr,
pub protocol: ServerProtocol,
}
impl Event for ServerStartedEvent {}
#[cfg(test)]
mod tests {
use super::{BuilderEventPublisher, Event, EventSubscriber};
use crate::app::AppBuilder;
use crate::error::Result;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
use tokio::sync::Mutex;
#[derive(Clone)]
struct TestEvent(usize);
impl Event for TestEvent {}
#[derive(Clone)]
struct OtherEvent;
impl Event for OtherEvent {}
#[tokio::test]
async fn publish_dispatches_to_matching_listeners() -> Result<()> {
let mut app = AppBuilder::default();
let total = Arc::new(AtomicUsize::new(0));
let total_ref = total.clone();
app.listen(move |event: TestEvent, _app: &mut AppBuilder| {
let total = total_ref.clone();
async move {
total.fetch_add(event.0, Ordering::SeqCst);
Ok(())
}
});
app.publish(TestEvent(3)).await?;
assert_eq!(total.load(Ordering::SeqCst), 3);
Ok(())
}
#[tokio::test]
async fn publish_keeps_event_types_isolated() -> Result<()> {
let mut app = AppBuilder::default();
let total = Arc::new(AtomicUsize::new(0));
let total_ref = total.clone();
app.listen(move |_: TestEvent, _app: &mut AppBuilder| {
let total = total_ref.clone();
async move {
total.fetch_add(1, Ordering::SeqCst);
Ok(())
}
});
app.publish(OtherEvent).await?;
assert_eq!(total.load(Ordering::SeqCst), 0);
Ok(())
}
#[tokio::test]
async fn publish_dispatches_listeners_in_registration_order() -> Result<()> {
let mut app = AppBuilder::default();
let calls = Arc::new(Mutex::new(Vec::new()));
let first = calls.clone();
app.listen(move |_: TestEvent, _app: &mut AppBuilder| {
let calls = first.clone();
async move {
calls.lock().await.push(1);
Ok(())
}
});
let second = calls.clone();
app.listen(move |_: TestEvent, _app: &mut AppBuilder| {
let calls = second.clone();
async move {
calls.lock().await.push(2);
Ok(())
}
});
app.publish(TestEvent(0)).await?;
assert_eq!(*calls.lock().await, vec![1, 2]);
Ok(())
}
}