rainier_events/event.rs
1//! The [`Event`] and [`Listener`] contracts.
2
3use std::any::{type_name, Any};
4use std::future::Future;
5use std::sync::Arc;
6
7use rainier_support::{BoxedFuture, Result};
8
9/// Something that happened, which listeners may react to.
10///
11/// Any plain object can be an event — a blanket impl covers
12/// every eligible type, so there is no `impl Event for OrderShipped` to write:
13///
14/// ```
15/// # use rainier_events::Event;
16/// struct OrderShipped { pub order_id: u64 }
17///
18/// assert!(OrderShipped::event_name().ends_with("OrderShipped"));
19/// ```
20///
21/// The bounds are what dispatch requires: `Send + Sync` because listeners may
22/// run on any task, and `'static` because the event is type-erased into an
23/// `Arc<dyn Any>` to reach listeners registered for its type.
24///
25/// The cost of the blanket impl is that [`event_name`](Event::event_name)
26/// cannot be overridden per type — an impl for your type would conflict with
27/// it. When the derived Rust path is not the name you want a wildcard listener
28/// to see, dispatch it explicitly with
29/// [`Dispatcher::dispatch_as`](crate::Dispatcher::dispatch_as).
30pub trait Event: Send + Sync + 'static {
31 /// A human-readable name, used by wildcard listeners and log output.
32 /// Always the Rust type path; see the trait docs.
33 fn event_name() -> &'static str
34 where
35 Self: Sized,
36 {
37 type_name::<Self>()
38 }
39}
40
41impl<T: Send + Sync + 'static> Event for T {}
42
43/// A reaction to an event of type `E`.
44///
45/// Listeners take `Arc<E>` rather than `&E` so their returned futures own
46/// what they need and can be `'static`. A borrowing signature would force
47/// every listener's future to be tied to the `dispatch` call's stack frame,
48/// which rules out boxing them into one heterogeneous list — and sharing one
49/// `Arc` across listeners avoids cloning the event per listener besides.
50pub trait Listener<E: Event>: Send + Sync + 'static {
51 /// Handle the event.
52 ///
53 /// Returning `Err` stops the remaining listeners for this event; see
54 /// [`Dispatcher::dispatch`](crate::Dispatcher::dispatch).
55 fn handle(&self, event: Arc<E>) -> BoxedFuture<Result<()>>;
56}
57
58/// Any `async` closure taking `Arc<E>` is a listener.
59///
60/// ```
61/// # use rainier_events::Dispatcher;
62/// # use std::sync::Arc;
63/// # struct UserRegistered { email: String }
64/// # #[tokio::main] async fn main() -> rainier_support::Result<()> {
65/// let events = Dispatcher::new();
66/// events.listen(|event: Arc<UserRegistered>| async move {
67/// println!("welcome {}", event.email);
68/// Ok(())
69/// });
70/// # events.dispatch(UserRegistered { email: "a@b.c".into() }).await?;
71/// # Ok(()) }
72/// ```
73impl<E, F, Fut> Listener<E> for F
74where
75 E: Event,
76 F: Fn(Arc<E>) -> Fut + Send + Sync + 'static,
77 Fut: Future<Output = Result<()>> + Send + 'static,
78{
79 fn handle(&self, event: Arc<E>) -> BoxedFuture<Result<()>> {
80 Box::pin(self(event))
81 }
82}
83
84/// A listener registered for *every* event, receiving the event's name and its
85/// type-erased payload.
86///
87/// The payload can be recovered with
88/// [`downcast`](std::sync::Arc::downcast) when the listener knows the type;
89/// most wildcard listeners (logging, metrics, an audit trail) only need the
90/// name.
91pub trait WildcardListener: Send + Sync + 'static {
92 /// Handle any dispatched event.
93 fn handle(
94 &self,
95 name: &'static str,
96 event: Arc<dyn Any + Send + Sync>,
97 ) -> BoxedFuture<Result<()>>;
98}
99
100impl<F, Fut> WildcardListener for F
101where
102 F: Fn(&'static str, Arc<dyn Any + Send + Sync>) -> Fut + Send + Sync + 'static,
103 Fut: Future<Output = Result<()>> + Send + 'static,
104{
105 fn handle(
106 &self,
107 name: &'static str,
108 event: Arc<dyn Any + Send + Sync>,
109 ) -> BoxedFuture<Result<()>> {
110 Box::pin(self(name, event))
111 }
112}
113
114/// A type that registers several related listeners at once — an event
115/// subscriber.
116///
117/// Keeps a feature's event wiring in one place instead of scattering
118/// `listen` calls across a service provider.
119///
120/// ```
121/// # use rainier_events::{Dispatcher, EventSubscriber};
122/// # use std::sync::Arc;
123/// # struct OrderPlaced; struct OrderShipped;
124/// struct OrderNotifications;
125///
126/// impl EventSubscriber for OrderNotifications {
127/// fn subscribe(&self, events: &Dispatcher) {
128/// events.listen(|_: Arc<OrderPlaced>| async { Ok(()) });
129/// events.listen(|_: Arc<OrderShipped>| async { Ok(()) });
130/// }
131/// }
132/// ```
133pub trait EventSubscriber {
134 /// Register this subscriber's listeners.
135 fn subscribe(&self, dispatcher: &crate::Dispatcher);
136}