Skip to main content

summer/
event.rs

1//! Application event support.
2
3use crate::{
4    app::{App, AppBuilder},
5    config::env::Env,
6    error::Result,
7    plugin::ComponentRegistry,
8};
9use async_trait::async_trait;
10use dashmap::DashMap;
11use std::{
12    any::{Any, TypeId},
13    future::Future,
14    net::SocketAddr,
15    path::PathBuf,
16    sync::Arc,
17};
18
19/// Build-phase event listener with access to [`AppBuilder`].
20#[async_trait]
21pub trait BuilderEventListener: Send + Sync {
22    /// Handles a type-erased event during application construction.
23    async fn on_event(
24        &self,
25        event: Arc<dyn Any + Send + Sync>,
26        app: &mut AppBuilder,
27    ) -> Result<()>;
28}
29
30struct TypedBuilderListener<E, F> {
31    listener: F,
32    _marker: std::marker::PhantomData<fn(E)>,
33}
34
35#[async_trait]
36impl<E, F, Fut> BuilderEventListener for TypedBuilderListener<E, F>
37where
38    E: Event,
39    F: Fn(E, &mut AppBuilder) -> Fut + Send + Sync,
40    Fut: Future<Output = Result<()>> + Send,
41{
42    async fn on_event(
43        &self,
44        event: Arc<dyn Any + Send + Sync>,
45        app: &mut AppBuilder,
46    ) -> Result<()> {
47        let event = event
48            .downcast::<E>()
49            .expect("event listener received unexpected event type");
50        (self.listener)((*event).clone(), app).await
51    }
52}
53
54type BuilderListener = Arc<dyn BuilderEventListener>;
55
56/// Runtime event listener with access to [`App`].
57#[async_trait]
58pub trait AppEventListener: Send + Sync {
59    /// Handles a type-erased event after the application is built.
60    async fn on_event(&self, event: Arc<dyn Any + Send + Sync>, app: &App) -> Result<()>;
61}
62
63struct TypedAppListener<E, F> {
64    listener: F,
65    _marker: std::marker::PhantomData<fn(E)>,
66}
67
68#[async_trait]
69impl<E, F, Fut> AppEventListener for TypedAppListener<E, F>
70where
71    E: Event,
72    F: Fn(E, &App) -> Fut + Send + Sync,
73    Fut: Future<Output = Result<()>> + Send,
74{
75    async fn on_event(&self, event: Arc<dyn Any + Send + Sync>, app: &App) -> Result<()> {
76        let event = event
77            .downcast::<E>()
78            .expect("event listener received unexpected event type");
79        (self.listener)((*event).clone(), app).await
80    }
81}
82
83type AppListener = Arc<dyn AppEventListener>;
84
85/// Marker trait for events that can be published through [`EventBus`].
86pub trait Event: Clone + Send + Sync + 'static {}
87
88/// Strongly typed asynchronous event bus.
89#[derive(Clone, Default)]
90pub struct EventBus {
91    builder_listeners: Arc<DashMap<TypeId, Vec<BuilderListener>>>,
92    app_listeners: Arc<DashMap<TypeId, Vec<AppListener>>>,
93}
94
95impl EventBus {
96    /// Registers a type-erased build-phase listener.
97    pub fn listen_dyn<E>(&self, listener: BuilderListener)
98    where
99        E: Event,
100    {
101        self.builder_listeners
102            .entry(TypeId::of::<E>())
103            .or_default()
104            .push(listener);
105    }
106
107    /// Registers a type-erased runtime listener.
108    pub fn listen_app_dyn<E>(&self, listener: AppListener)
109    where
110        E: Event,
111    {
112        self.app_listeners
113            .entry(TypeId::of::<E>())
114            .or_default()
115            .push(listener);
116    }
117
118    /// Registers a build-phase listener that receives [`AppBuilder`].
119    pub fn listen<E, F, Fut>(&self, listener: F)
120    where
121        E: Event,
122        F: Fn(E, &mut AppBuilder) -> Fut + Send + Sync + 'static,
123        Fut: Future<Output = Result<()>> + Send,
124    {
125        self.builder_listeners
126            .entry(TypeId::of::<E>())
127            .or_default()
128            .push(Arc::new(TypedBuilderListener {
129                listener,
130                _marker: std::marker::PhantomData,
131            }));
132    }
133
134    /// Registers a runtime listener that receives [`App`].
135    pub fn listen_app<E, F, Fut>(&self, listener: F)
136    where
137        E: Event,
138        F: Fn(E, &App) -> Fut + Send + Sync + 'static,
139        Fut: Future<Output = Result<()>> + Send,
140    {
141        self.app_listeners
142            .entry(TypeId::of::<E>())
143            .or_default()
144            .push(Arc::new(TypedAppListener {
145                listener,
146                _marker: std::marker::PhantomData,
147            }));
148    }
149
150    /// Publishes an event to build-phase listeners.
151    pub async fn publish_builder<E>(&self, event: E, app: &mut AppBuilder) -> Result<()>
152    where
153        E: Event,
154    {
155        let listeners = self
156            .builder_listeners
157            .get(&TypeId::of::<E>())
158            .map(|entry| entry.clone())
159            .unwrap_or_default();
160
161        let event = Arc::new(event) as Arc<dyn Any + Send + Sync>;
162        for listener in listeners {
163            listener.on_event(event.clone(), app).await?;
164        }
165
166        Ok(())
167    }
168
169    /// Publishes an event to runtime listeners.
170    pub async fn publish_app<E>(&self, event: E, app: &App) -> Result<()>
171    where
172        E: Event,
173    {
174        let listeners = self
175            .app_listeners
176            .get(&TypeId::of::<E>())
177            .map(|entry| entry.clone())
178            .unwrap_or_default();
179
180        let event = Arc::new(event) as Arc<dyn Any + Send + Sync>;
181        for listener in listeners {
182            listener.on_event(event.clone(), app).await?;
183        }
184
185        Ok(())
186    }
187}
188
189/// Publishes events during application build (`AppBuilder` phase).
190#[async_trait]
191pub trait BuilderEventPublisher {
192    /// Publishes an event to build-phase listeners.
193    async fn publish<E>(&mut self, event: E) -> Result<()>
194    where
195        E: Event;
196}
197
198/// Publishes events on a running [`App`].
199#[async_trait]
200pub trait EventPublisher: Sync {
201    /// Publishes an event to runtime listeners.
202    async fn publish<E>(&self, event: E) -> Result<()>
203    where
204        E: Event;
205}
206
207/// Subscribes to build-phase events on [`AppBuilder`].
208pub trait EventSubscriber {
209    /// Registers a type-erased build-phase listener.
210    fn listen_dyn<E>(&self, listener: Arc<dyn BuilderEventListener>)
211    where
212        E: Event;
213
214    /// Registers a listener invoked with the event and [`AppBuilder`].
215    fn listen<E, F, Fut>(&self, listener: F)
216    where
217        E: Event,
218        F: Fn(E, &mut AppBuilder) -> Fut + Send + Sync + 'static,
219        Fut: Future<Output = Result<()>> + Send;
220}
221
222/// Subscribes to runtime events (after [`App`] is built).
223pub trait AppEventSubscriber {
224    /// Registers a type-erased runtime listener.
225    fn listen_app_dyn<E>(&self, listener: Arc<dyn AppEventListener>)
226    where
227        E: Event;
228
229    /// Registers a listener invoked with the event and [`App`].
230    fn listen_app<E, F, Fut>(&self, listener: F)
231    where
232        E: Event,
233        F: Fn(E, &App) -> Fut + Send + Sync + 'static,
234        Fut: Future<Output = Result<()>> + Send;
235}
236
237#[async_trait]
238impl BuilderEventPublisher for AppBuilder {
239    async fn publish<E>(&mut self, event: E) -> Result<()>
240    where
241        E: Event,
242    {
243        let bus = self.get_expect_component::<EventBus>().clone();
244        bus.publish_builder(event, self).await
245    }
246}
247
248#[async_trait]
249impl EventPublisher for App {
250    async fn publish<E>(&self, event: E) -> Result<()>
251    where
252        E: Event,
253    {
254        self.get_expect_component::<EventBus>()
255            .publish_app(event, self)
256            .await
257    }
258}
259
260impl EventSubscriber for AppBuilder {
261    fn listen_dyn<E>(&self, listener: Arc<dyn BuilderEventListener>)
262    where
263        E: Event,
264    {
265        self.get_expect_component::<EventBus>()
266            .listen_dyn::<E>(listener);
267    }
268
269    fn listen<E, F, Fut>(&self, listener: F)
270    where
271        E: Event,
272        F: Fn(E, &mut AppBuilder) -> Fut + Send + Sync + 'static,
273        Fut: Future<Output = Result<()>> + Send,
274    {
275        self.get_expect_component::<EventBus>().listen(listener);
276    }
277}
278
279impl AppEventSubscriber for AppBuilder {
280    fn listen_app_dyn<E>(&self, listener: Arc<dyn AppEventListener>)
281    where
282        E: Event,
283    {
284        self.get_expect_component::<EventBus>()
285            .listen_app_dyn::<E>(listener);
286    }
287
288    fn listen_app<E, F, Fut>(&self, listener: F)
289    where
290        E: Event,
291        F: Fn(E, &App) -> Fut + Send + Sync + 'static,
292        Fut: Future<Output = Result<()>> + Send,
293    {
294        self.get_expect_component::<EventBus>().listen_app(listener);
295    }
296}
297
298impl AppEventSubscriber for App {
299    fn listen_app_dyn<E>(&self, listener: Arc<dyn AppEventListener>)
300    where
301        E: Event,
302    {
303        self.get_expect_component::<EventBus>()
304            .listen_app_dyn::<E>(listener);
305    }
306
307    fn listen_app<E, F, Fut>(&self, listener: F)
308    where
309        E: Event,
310        F: Fn(E, &App) -> Fut + Send + Sync + 'static,
311        Fut: Future<Output = Result<()>> + Send,
312    {
313        self.get_expect_component::<EventBus>().listen_app(listener);
314    }
315}
316
317/// Describes the source used to initialize application configuration.
318#[derive(Debug, Clone, PartialEq, Eq)]
319pub enum ConfigSource {
320    /// Configuration loaded from a TOML file.
321    File(PathBuf),
322    /// Configuration loaded from an inline TOML string.
323    Inline,
324}
325
326/// Published when local configuration is loaded; listeners may merge remote config into [`AppBuilder`].
327#[derive(Debug, Clone)]
328pub struct ConfigEvent {
329    /// Currently active application environment.
330    pub env: Env,
331    /// Source used to load the current configuration.
332    pub source: ConfigSource,
333}
334
335impl Event for ConfigEvent {}
336
337/// Published after all plugins have been built.
338#[derive(Debug, Clone)]
339pub struct PluginsBuiltEvent;
340
341impl Event for PluginsBuiltEvent {}
342
343/// Published after service dependency injection has completed.
344#[derive(Debug, Clone)]
345pub struct ServicesInjectedEvent;
346
347impl Event for ServicesInjectedEvent {}
348
349/// Published after the application has been built and installed globally.
350#[derive(Clone)]
351pub struct AppBuiltEvent {
352    /// Built application instance.
353    pub app: Arc<App>,
354}
355
356impl Event for AppBuiltEvent {}
357
358/// Shutdown lifecycle phase.
359#[derive(Debug, Clone, Copy, PartialEq, Eq)]
360pub enum ShutdownPhase {
361    /// Shutdown hooks are about to run.
362    BeforeHooks,
363    /// Shutdown hooks have completed.
364    AfterHooks,
365}
366
367/// Published while the application is shutting down.
368#[derive(Debug, Clone)]
369pub struct ShutdownEvent {
370    /// Current shutdown phase.
371    pub phase: ShutdownPhase,
372}
373
374impl Event for ShutdownEvent {}
375
376/// Protocol of a server that has started listening.
377#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
378pub enum ServerProtocol {
379    /// HTTP server ([`summer-web`](https://docs.rs/summer-web)).
380    Http,
381    /// gRPC server ([`summer-grpc`](https://docs.rs/summer-grpc)).
382    Grpc,
383}
384
385impl ServerProtocol {
386    /// Canonical metadata value for Nacos instance metadata (`protocol` key).
387    pub fn as_str(self) -> &'static str {
388        match self {
389            Self::Http => "http",
390            Self::Grpc => "grpc",
391        }
392    }
393}
394
395/// Published when a server (HTTP, gRPC, …) is ready to accept requests.
396///
397/// Plugins such as [`summer-web`](https://docs.rs/summer-web) and
398/// [`summer-grpc`](https://docs.rs/summer-grpc) each publish once after bind.
399/// Listeners (e.g. service discovery) may run once per protocol in the same process.
400#[derive(Debug, Clone)]
401pub struct ServerStartedEvent {
402    /// Bound socket address (from the plugin config, often before `serve` blocks).
403    pub addr: SocketAddr,
404    /// Which protocol stack started; used for metadata and multi-port registration.
405    pub protocol: ServerProtocol,
406}
407
408impl Event for ServerStartedEvent {}
409
410#[cfg(test)]
411mod tests {
412    use super::{BuilderEventPublisher, Event, EventSubscriber};
413    use crate::app::AppBuilder;
414    use crate::error::Result;
415    use std::sync::{
416        atomic::{AtomicUsize, Ordering},
417        Arc,
418    };
419    use tokio::sync::Mutex;
420
421    #[derive(Clone)]
422    struct TestEvent(usize);
423
424    impl Event for TestEvent {}
425
426    #[derive(Clone)]
427    struct OtherEvent;
428
429    impl Event for OtherEvent {}
430
431    #[tokio::test]
432    async fn publish_dispatches_to_matching_listeners() -> Result<()> {
433        let mut app = AppBuilder::default();
434        let total = Arc::new(AtomicUsize::new(0));
435        let total_ref = total.clone();
436
437        app.listen(move |event: TestEvent, _app: &mut AppBuilder| {
438            let total = total_ref.clone();
439            async move {
440                total.fetch_add(event.0, Ordering::SeqCst);
441                Ok(())
442            }
443        });
444
445        app.publish(TestEvent(3)).await?;
446        assert_eq!(total.load(Ordering::SeqCst), 3);
447        Ok(())
448    }
449
450    #[tokio::test]
451    async fn publish_keeps_event_types_isolated() -> Result<()> {
452        let mut app = AppBuilder::default();
453        let total = Arc::new(AtomicUsize::new(0));
454        let total_ref = total.clone();
455
456        app.listen(move |_: TestEvent, _app: &mut AppBuilder| {
457            let total = total_ref.clone();
458            async move {
459                total.fetch_add(1, Ordering::SeqCst);
460                Ok(())
461            }
462        });
463
464        app.publish(OtherEvent).await?;
465        assert_eq!(total.load(Ordering::SeqCst), 0);
466        Ok(())
467    }
468
469    #[tokio::test]
470    async fn publish_dispatches_listeners_in_registration_order() -> Result<()> {
471        let mut app = AppBuilder::default();
472        let calls = Arc::new(Mutex::new(Vec::new()));
473
474        let first = calls.clone();
475        app.listen(move |_: TestEvent, _app: &mut AppBuilder| {
476            let calls = first.clone();
477            async move {
478                calls.lock().await.push(1);
479                Ok(())
480            }
481        });
482
483        let second = calls.clone();
484        app.listen(move |_: TestEvent, _app: &mut AppBuilder| {
485            let calls = second.clone();
486            async move {
487                calls.lock().await.push(2);
488                Ok(())
489            }
490        });
491
492        app.publish(TestEvent(0)).await?;
493        assert_eq!(*calls.lock().await, vec![1, 2]);
494        Ok(())
495    }
496}