Skip to main content

rskit_messaging/
handler.rs

1//! Message handler trait and middleware chain for consumed messages.
2
3use std::sync::Arc;
4
5use async_trait::async_trait;
6use rskit_errors::AppResult;
7
8use crate::message::Message;
9
10/// Handler for processing consumed messages.
11///
12/// Implement this trait to define how incoming messages are processed.
13/// For simple cases, use [`FnHandler`] to wrap a closure.
14#[async_trait]
15pub trait MessageHandler<T: Send + Sync + 'static>: Send + Sync + 'static {
16    /// Process a single message.
17    async fn handle(&self, msg: Message<T>) -> AppResult<()>;
18}
19
20/// Middleware that wraps a handler with cross-cutting concerns.
21///
22/// Each middleware receives the next handler in the chain and returns
23/// a new handler that adds behaviour around it (logging, metrics,
24/// error recovery, etc.).
25pub trait HandlerMiddleware<T: Send + Sync + 'static>: Send + Sync + 'static {
26    /// Wrap the given handler, returning a new handler that adds
27    /// middleware behaviour.
28    fn wrap(&self, next: Arc<dyn MessageHandler<T>>) -> Arc<dyn MessageHandler<T>>;
29}
30
31/// Chains middleware around a base handler.
32///
33/// Middlewares are applied in order: the first middleware in the slice
34/// becomes the outermost wrapper.
35///
36/// ```text
37/// chain_handlers(base, [mw_a, mw_b])
38///   => mw_a(mw_b(base))
39/// ```
40pub fn chain_handlers<T: Send + Sync + 'static>(
41    base: Arc<dyn MessageHandler<T>>,
42    middlewares: &[Arc<dyn HandlerMiddleware<T>>],
43) -> Arc<dyn MessageHandler<T>> {
44    let mut handler = base;
45    for mw in middlewares.iter().rev() {
46        handler = mw.wrap(handler);
47    }
48    handler
49}
50
51/// Adapter that turns a closure into a [`HandlerMiddleware`].
52///
53/// Useful for simple one-off middleware that do not need their own struct.
54///
55/// # Example
56///
57/// ```rust,ignore
58/// use rskit_messaging::handler::middleware_fn;
59/// use std::sync::Arc;
60///
61/// let mw = middleware_fn(|next| {
62///     // wrap next handler with custom behaviour
63///     next
64/// });
65/// ```
66pub fn middleware_fn<T, F>(f: F) -> impl HandlerMiddleware<T>
67where
68    T: Send + Sync + 'static,
69    F: Fn(Arc<dyn MessageHandler<T>>) -> Arc<dyn MessageHandler<T>> + Send + Sync + 'static,
70{
71    FnMiddleware {
72        func: f,
73        _marker: std::marker::PhantomData,
74    }
75}
76
77struct FnMiddleware<T, F> {
78    func: F,
79    _marker: std::marker::PhantomData<T>,
80}
81
82impl<T, F> HandlerMiddleware<T> for FnMiddleware<T, F>
83where
84    T: Send + Sync + 'static,
85    F: Fn(Arc<dyn MessageHandler<T>>) -> Arc<dyn MessageHandler<T>> + Send + Sync + 'static,
86{
87    fn wrap(&self, next: Arc<dyn MessageHandler<T>>) -> Arc<dyn MessageHandler<T>> {
88        (self.func)(next)
89    }
90}
91
92/// Adapter that turns an async closure into a [`MessageHandler`].
93///
94/// # Example
95///
96/// ```rust,ignore
97/// use rskit_messaging::handler::FnHandler;
98///
99/// let handler = FnHandler::new(|msg| async move {
100///     println!("got: {:?}", msg.payload);
101///     Ok(())
102/// });
103/// ```
104pub struct FnHandler<T, F, Fut>
105where
106    T: Send + Sync + 'static,
107    F: Fn(Message<T>) -> Fut + Send + Sync + 'static,
108    Fut: std::future::Future<Output = AppResult<()>> + Send + 'static,
109{
110    func: F,
111    _marker: std::marker::PhantomData<T>,
112}
113
114impl<T, F, Fut> FnHandler<T, F, Fut>
115where
116    T: Send + Sync + 'static,
117    F: Fn(Message<T>) -> Fut + Send + Sync + 'static,
118    Fut: std::future::Future<Output = AppResult<()>> + Send + 'static,
119{
120    /// Create a new function handler from the given closure.
121    pub const fn new(func: F) -> Self {
122        Self {
123            func,
124            _marker: std::marker::PhantomData,
125        }
126    }
127}
128
129#[async_trait]
130impl<T, F, Fut> MessageHandler<T> for FnHandler<T, F, Fut>
131where
132    T: Send + Sync + 'static,
133    F: Fn(Message<T>) -> Fut + Send + Sync + 'static,
134    Fut: std::future::Future<Output = AppResult<()>> + Send + 'static,
135{
136    async fn handle(&self, msg: Message<T>) -> AppResult<()> {
137        (self.func)(msg).await
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use std::sync::atomic::{AtomicU32, Ordering};
144
145    use super::*;
146
147    #[tokio::test]
148    async fn fn_handler_processes_message() {
149        let counter = Arc::new(AtomicU32::new(0));
150        let c = counter.clone();
151        let handler = FnHandler::new(move |_msg: Message<String>| {
152            let c = c.clone();
153            async move {
154                c.fetch_add(1, Ordering::SeqCst);
155                Ok(())
156            }
157        });
158
159        let msg = Message::new("t", "hello".to_string());
160        handler.handle(msg).await.unwrap();
161        assert_eq!(counter.load(Ordering::SeqCst), 1);
162    }
163
164    /// A middleware that increments a counter before delegating.
165    struct CountingMiddleware {
166        counter: Arc<AtomicU32>,
167    }
168
169    struct CountingHandler<T: Send + Sync + 'static> {
170        counter: Arc<AtomicU32>,
171        next: Arc<dyn MessageHandler<T>>,
172    }
173
174    #[async_trait]
175    impl<T: Send + Sync + 'static> MessageHandler<T> for CountingHandler<T> {
176        async fn handle(&self, msg: Message<T>) -> AppResult<()> {
177            self.counter.fetch_add(1, Ordering::SeqCst);
178            self.next.handle(msg).await
179        }
180    }
181
182    impl<T: Send + Sync + 'static> HandlerMiddleware<T> for CountingMiddleware {
183        fn wrap(&self, next: Arc<dyn MessageHandler<T>>) -> Arc<dyn MessageHandler<T>> {
184            Arc::new(CountingHandler {
185                counter: self.counter.clone(),
186                next,
187            })
188        }
189    }
190
191    #[tokio::test]
192    async fn middleware_chain_applies_in_order() {
193        let mw_counter = Arc::new(AtomicU32::new(0));
194        let base_counter = Arc::new(AtomicU32::new(0));
195
196        let bc = base_counter.clone();
197        let base: Arc<dyn MessageHandler<String>> =
198            Arc::new(FnHandler::new(move |_msg: Message<String>| {
199                let bc = bc.clone();
200                async move {
201                    bc.fetch_add(1, Ordering::SeqCst);
202                    Ok(())
203                }
204            }));
205
206        let mw: Arc<dyn HandlerMiddleware<String>> = Arc::new(CountingMiddleware {
207            counter: mw_counter.clone(),
208        });
209
210        let chained = chain_handlers(base, &[mw]);
211        let msg = Message::new("t", "data".to_string());
212        chained.handle(msg).await.unwrap();
213
214        assert_eq!(mw_counter.load(Ordering::SeqCst), 1);
215        assert_eq!(base_counter.load(Ordering::SeqCst), 1);
216    }
217}