Skip to main content

a3s_boot/pipeline/
interceptor.rs

1use super::ExecutionContext;
2use crate::{BootResponse, BoxFuture, Result};
3use std::sync::Arc;
4
5/// Runs around the handler for cross-cutting behavior.
6pub trait Interceptor: Send + Sync + 'static {
7    fn before(&self, _context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
8        Box::pin(async { Ok(()) })
9    }
10
11    fn short_circuit(
12        &self,
13        _context: ExecutionContext,
14    ) -> BoxFuture<'static, Result<Option<BootResponse>>> {
15        Box::pin(async { Ok(None) })
16    }
17
18    fn after(
19        &self,
20        _context: ExecutionContext,
21        response: BootResponse,
22    ) -> BoxFuture<'static, Result<BootResponse>> {
23        Box::pin(async move { Ok(response) })
24    }
25}
26
27impl<F, Fut> Interceptor for F
28where
29    F: Fn(ExecutionContext) -> Fut + Send + Sync + 'static,
30    Fut: std::future::Future<Output = Result<()>> + Send + 'static,
31{
32    fn before(&self, context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
33        Box::pin(self(context))
34    }
35}
36
37/// Protocol-neutral observer that can run around HTTP, WebSocket, or transport handlers.
38pub trait ExecutionInterceptor: Send + Sync + 'static {
39    fn before(&self, _context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
40        Box::pin(async { Ok(()) })
41    }
42
43    fn after(&self, _context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
44        Box::pin(async { Ok(()) })
45    }
46}
47
48impl<F, Fut> ExecutionInterceptor for F
49where
50    F: Fn(ExecutionContext) -> Fut + Send + Sync + 'static,
51    Fut: std::future::Future<Output = Result<()>> + Send + 'static,
52{
53    fn before(&self, context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
54        Box::pin(self(context))
55    }
56}
57
58impl ExecutionInterceptor for Arc<dyn ExecutionInterceptor> {
59    fn before(&self, context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
60        self.as_ref().before(context)
61    }
62
63    fn after(&self, context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
64        self.as_ref().after(context)
65    }
66}
67
68pub(crate) struct ExecutionInterceptorAdapter<I> {
69    inner: I,
70}
71
72impl<I> ExecutionInterceptorAdapter<I> {
73    pub(crate) fn new(inner: I) -> Self {
74        Self { inner }
75    }
76}
77
78impl<I> Interceptor for ExecutionInterceptorAdapter<I>
79where
80    I: ExecutionInterceptor,
81{
82    fn before(&self, context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
83        self.inner.before(context)
84    }
85
86    fn after(
87        &self,
88        context: ExecutionContext,
89        response: BootResponse,
90    ) -> BoxFuture<'static, Result<BootResponse>> {
91        let future = self.inner.after(context);
92        Box::pin(async move {
93            future.await?;
94            Ok(response)
95        })
96    }
97}