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