Skip to main content

caelix_core/
events.rs

1use crate::{BoxFuture, Container, Injectable, Module, ModuleMetadata, Result};
2use futures_util::{StreamExt, stream, stream::BoxStream};
3use std::{
4    any::{Any, TypeId},
5    collections::HashMap,
6    marker::PhantomData,
7    sync::{Arc, RwLock},
8};
9use tokio::sync::broadcast;
10use tokio_stream::wrappers::BroadcastStream;
11use tokio_stream::wrappers::errors::BroadcastStreamRecvError;
12
13/// Default capacity for per-event-type broadcast channels used by [`EventBus::subscribe`].
14const EVENT_BROADCAST_CAPACITY: usize = 256;
15
16#[diagnostic::on_unimplemented(
17    message = "`{Self}` is not an event handler for `{E}`",
18    label = "missing `impl EventHandler<{E}> for {Self}`",
19    note = "add `impl EventHandler<{E}> for {Self}` or use the correct event type in `.event_handler_for::<Event, Handler>()`"
20)]
21pub trait EventHandler<E>: Send + Sync + 'static {
22    fn handle(&self, event: E) -> BoxFuture<'_, Result<()>>;
23}
24
25#[diagnostic::on_unimplemented(
26    message = "`{Self}` cannot be registered with `.event_handler::<{Self}>()` yet",
27    label = "missing `impl RegisterableEventHandler for {Self}`",
28    note = "add `impl RegisterableEventHandler for {Self} {{ type Event = YourEvent; }}` or use `.event_handler_for::<YourEvent, {Self}>()`"
29)]
30pub trait RegisterableEventHandler: Injectable {
31    type Event: Clone + Send + Sync + 'static;
32
33    fn register_into(handler: Arc<Self>, bus: &EventBus) -> Result<()>
34    where
35        Self: Sized + EventHandler<Self::Event>,
36    {
37        bus.register::<Self::Event, Self>(handler)
38    }
39}
40
41trait ErasedHandler: Send + Sync {
42    fn handle_erased(&self, event: &dyn Any) -> BoxFuture<'_, Result<()>>;
43}
44
45struct TypedHandler<E, H> {
46    handler: Arc<H>,
47    _marker: PhantomData<E>,
48}
49
50impl<E, H> ErasedHandler for TypedHandler<E, H>
51where
52    E: Clone + Send + Sync + 'static,
53    H: EventHandler<E>,
54{
55    fn handle_erased(&self, event: &dyn Any) -> BoxFuture<'_, Result<()>> {
56        let event = match event.downcast_ref::<E>() {
57            Some(event) => event.clone(),
58            None => {
59                return Box::pin(async {
60                    Err(crate::exception::startup_error("event type mismatch"))
61                });
62            }
63        };
64        let handler = self.handler.clone();
65
66        Box::pin(async move { handler.handle(event).await })
67    }
68}
69
70struct EventChannel<E> {
71    tx: broadcast::Sender<E>,
72}
73
74pub struct EventBus {
75    handlers: RwLock<HashMap<TypeId, Vec<Arc<dyn ErasedHandler>>>>,
76    broadcasts: RwLock<HashMap<TypeId, Box<dyn Any + Send + Sync>>>,
77}
78
79impl EventBus {
80    pub fn new() -> Self {
81        Self {
82            handlers: RwLock::new(HashMap::new()),
83            broadcasts: RwLock::new(HashMap::new()),
84        }
85    }
86
87    pub fn register<E, H>(&self, handler: Arc<H>) -> Result<()>
88    where
89        E: Clone + Send + Sync + 'static,
90        H: EventHandler<E>,
91    {
92        let wrapped = Arc::new(TypedHandler::<E, H> {
93            handler,
94            _marker: PhantomData,
95        });
96
97        self.handlers
98            .write()
99            .map_err(|_| crate::exception::startup_error("event handler registry lock poisoned"))?
100            .entry(TypeId::of::<E>())
101            .or_default()
102            .push(wrapped);
103        Ok(())
104    }
105
106    /// Create the per-type broadcast channel if missing; used only by [`Self::subscribe`].
107    fn ensure_sender<E>(&self) -> Result<broadcast::Sender<E>>
108    where
109        E: Clone + Send + Sync + 'static,
110    {
111        let type_id = TypeId::of::<E>();
112
113        {
114            let broadcasts = self.broadcasts.read().map_err(|_| {
115                crate::exception::startup_error("event broadcast registry lock poisoned")
116            })?;
117            if let Some(channel) = broadcasts.get(&type_id) {
118                return channel
119                    .downcast_ref::<EventChannel<E>>()
120                    .ok_or_else(|| crate::exception::startup_error("event broadcast type mismatch"))
121                    .map(|channel| channel.tx.clone());
122            }
123        }
124
125        let mut broadcasts = self.broadcasts.write().map_err(|_| {
126            crate::exception::startup_error("event broadcast registry lock poisoned")
127        })?;
128        let channel = broadcasts.entry(type_id).or_insert_with(|| {
129            let (tx, _rx) = broadcast::channel::<E>(EVENT_BROADCAST_CAPACITY);
130            Box::new(EventChannel { tx })
131        });
132
133        channel
134            .downcast_ref::<EventChannel<E>>()
135            .ok_or_else(|| crate::exception::startup_error("event broadcast type mismatch"))
136            .map(|channel| channel.tx.clone())
137    }
138
139    /// Existing channel only — does not allocate. Used by [`Self::emit`].
140    fn existing_sender<E>(&self) -> Result<Option<broadcast::Sender<E>>>
141    where
142        E: Clone + Send + Sync + 'static,
143    {
144        self.broadcasts
145            .read()
146            .map_err(|_| crate::exception::startup_error("event broadcast registry lock poisoned"))?
147            .get(&TypeId::of::<E>())
148            .map(|channel| {
149                channel
150                    .downcast_ref::<EventChannel<E>>()
151                    .ok_or_else(|| crate::exception::startup_error("event broadcast type mismatch"))
152                    .map(|channel| channel.tx.clone())
153            })
154            .transpose()
155    }
156
157    /// Live stream of events of type `E`. Receives events after subscription;
158    /// earlier events are not replayed. Slow consumers may lag and drop events
159    /// (see broadcast capacity).
160    ///
161    /// Creates the broadcast channel for `E` on first subscribe. [`Self::emit`]
162    /// only fans out when a channel already exists (i.e. someone has subscribed).
163    pub fn subscribe<E>(&self) -> BoxStream<'static, Result<E>>
164    where
165        E: Clone + Send + Sync + 'static,
166    {
167        match self.ensure_sender::<E>() {
168            Ok(tx) => BroadcastStream::new(tx.subscribe())
169                .filter_map(|item| async move {
170                    match item {
171                        Ok(event) => Some(Ok(event)),
172                        Err(BroadcastStreamRecvError::Lagged(_)) => {
173                            tracing::warn!("event bus subscriber lagged; dropped events");
174                            None
175                        }
176                    }
177                })
178                .boxed(),
179            Err(err) => stream::once(async move { Err(err) }).boxed(),
180        }
181    }
182
183    /// Run registered handlers in order, then fan out to live subscribers.
184    ///
185    /// Handlers run first so a failed handler aborts `emit` and does **not**
186    /// publish to stream subscribers. Broadcast channels are only used when
187    /// created by a prior [`Self::subscribe`] — emit alone never allocates one.
188    pub async fn emit<E>(&self, event: E) -> Result<()>
189    where
190        E: Clone + Send + Sync + 'static,
191    {
192        let handlers = self
193            .handlers
194            .read()
195            .map_err(|_| crate::exception::startup_error("event handler registry lock poisoned"))?
196            .get(&TypeId::of::<E>())
197            .cloned();
198
199        if let Some(handlers) = handlers {
200            for handler in handlers {
201                handler.handle_erased(&event).await?;
202            }
203        }
204
205        if let Some(tx) = self.existing_sender::<E>()? {
206            let _ = tx.send(event);
207        }
208
209        Ok(())
210    }
211
212    pub fn handler_count<E>(&self) -> Result<usize>
213    where
214        E: Clone + Send + Sync + 'static,
215    {
216        Ok(self
217            .handlers
218            .read()
219            .map_err(|_| crate::exception::startup_error("event handler registry lock poisoned"))?
220            .get(&TypeId::of::<E>())
221            .map_or(0, Vec::len))
222    }
223}
224
225impl Default for EventBus {
226    fn default() -> Self {
227        Self::new()
228    }
229}
230
231impl Injectable for EventBus {
232    fn dependencies() -> Vec<crate::ProviderDependency> {
233        crate::provider_dependencies![]
234    }
235
236    fn create(_container: &Container) -> BoxFuture<'_, Result<Self>> {
237        Box::pin(async { Ok(Self::new()) })
238    }
239}
240
241pub struct EventModule;
242
243impl Module for EventModule {
244    fn register() -> ModuleMetadata {
245        ModuleMetadata::new()
246            .provider::<EventBus>()
247            .export::<EventBus>()
248    }
249}
250
251pub struct EventHandlerDef {
252    type_id: TypeId,
253    type_name: &'static str,
254    register_fn: Box<dyn Fn(&Container) -> Result<()> + Send + Sync>,
255}
256
257impl EventHandlerDef {
258    pub(crate) fn of<H>() -> Self
259    where
260        H: RegisterableEventHandler + EventHandler<H::Event>,
261    {
262        Self {
263            type_id: TypeId::of::<H>(),
264            type_name: std::any::type_name::<H>(),
265            register_fn: Box::new(|container| {
266                let handler = container.resolve::<H>()?;
267                let bus = container.resolve::<EventBus>()?;
268                H::register_into(handler, &bus)
269            }),
270        }
271    }
272
273    pub(crate) fn for_event<E, H>() -> Self
274    where
275        E: Clone + Send + Sync + 'static,
276        H: Injectable + EventHandler<E>,
277    {
278        Self {
279            type_id: TypeId::of::<H>(),
280            type_name: std::any::type_name::<H>(),
281            register_fn: Box::new(|container| {
282                let handler = container.resolve::<H>()?;
283                let bus = container.resolve::<EventBus>()?;
284                bus.register::<E, H>(handler)
285            }),
286        }
287    }
288
289    pub(crate) fn assert_registered_or_declared(
290        &self,
291        declared: &std::collections::HashSet<TypeId>,
292    ) -> Result<()> {
293        if declared.contains(&self.type_id) {
294            return Ok(());
295        }
296
297        Err(crate::exception::startup_error(format!(
298            "missing event handler provider at startup: {} was declared by module metadata but was not registered as a provider",
299            self.type_name
300        )))
301    }
302
303    pub(crate) fn register(&self, container: &Container) -> Result<()> {
304        (self.register_fn)(container)
305    }
306}