a3s_boot/pipeline/
interceptor.rs1use super::ExecutionContext;
2use crate::{BootError, BootResponse, BoxFuture, Result};
3use std::future::Future;
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::Arc;
6
7pub struct CallHandler<'a, T = BootResponse> {
15 call: Arc<dyn Fn() -> BoxFuture<'a, Result<T>> + Send + Sync + 'a>,
16 running: Arc<AtomicBool>,
17}
18
19impl<'a, T> Clone for CallHandler<'a, T> {
20 fn clone(&self) -> Self {
21 Self {
22 call: Arc::clone(&self.call),
23 running: Arc::clone(&self.running),
24 }
25 }
26}
27
28impl<'a, T> CallHandler<'a, T>
29where
30 T: Send + 'a,
31{
32 pub fn from_fn<F, Fut>(call: F) -> Self
38 where
39 F: Fn() -> Fut + Send + Sync + 'a,
40 Fut: Future<Output = Result<T>> + Send + 'a,
41 {
42 Self {
43 call: Arc::new(move || Box::pin(call())),
44 running: Arc::new(AtomicBool::new(false)),
45 }
46 }
47
48 pub fn handle(&self) -> BoxFuture<'a, Result<T>> {
53 let call = Arc::clone(&self.call);
54 let running = Arc::clone(&self.running);
55 Box::pin(async move {
56 if running
57 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
58 .is_err()
59 {
60 return Err(BootError::Internal(
61 "call handler is already running".to_string(),
62 ));
63 }
64
65 let _reset = CallHandlerReset {
66 running: Arc::clone(&running),
67 };
68 call().await
69 })
70 }
71}
72
73struct CallHandlerReset {
74 running: Arc<AtomicBool>,
75}
76
77impl Drop for CallHandlerReset {
78 fn drop(&mut self) {
79 self.running.store(false, Ordering::Release);
80 }
81}
82
83pub trait Interceptor: Send + Sync + 'static {
85 fn intercept<'a>(
92 &'a self,
93 context: ExecutionContext,
94 next: CallHandler<'a>,
95 ) -> BoxFuture<'a, Result<BootResponse>> {
96 Box::pin(async move {
97 self.before(context.clone()).await?;
98
99 if let Some(response) = self.short_circuit(context.clone()).await? {
100 return Ok(response);
101 }
102
103 let response = next.handle().await?;
104 self.after(context, response).await
105 })
106 }
107
108 fn before(&self, _context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
109 Box::pin(async { Ok(()) })
110 }
111
112 fn short_circuit(
113 &self,
114 _context: ExecutionContext,
115 ) -> BoxFuture<'static, Result<Option<BootResponse>>> {
116 Box::pin(async { Ok(None) })
117 }
118
119 fn after(
120 &self,
121 _context: ExecutionContext,
122 response: BootResponse,
123 ) -> BoxFuture<'static, Result<BootResponse>> {
124 Box::pin(async move { Ok(response) })
125 }
126}
127
128impl<F, Fut> Interceptor for F
129where
130 F: Fn(ExecutionContext) -> Fut + Send + Sync + 'static,
131 Fut: std::future::Future<Output = Result<()>> + Send + 'static,
132{
133 fn before(&self, context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
134 Box::pin(self(context))
135 }
136}
137
138pub trait ExecutionInterceptor: Send + Sync + 'static {
140 fn before(&self, _context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
141 Box::pin(async { Ok(()) })
142 }
143
144 fn after(&self, _context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
145 Box::pin(async { Ok(()) })
146 }
147}
148
149impl<F, Fut> ExecutionInterceptor for F
150where
151 F: Fn(ExecutionContext) -> Fut + Send + Sync + 'static,
152 Fut: std::future::Future<Output = Result<()>> + Send + 'static,
153{
154 fn before(&self, context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
155 Box::pin(self(context))
156 }
157}
158
159impl ExecutionInterceptor for Arc<dyn ExecutionInterceptor> {
160 fn before(&self, context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
161 self.as_ref().before(context)
162 }
163
164 fn after(&self, context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
165 self.as_ref().after(context)
166 }
167}
168
169pub(crate) struct ExecutionInterceptorAdapter<I> {
170 inner: I,
171}
172
173impl<I> ExecutionInterceptorAdapter<I> {
174 pub(crate) fn new(inner: I) -> Self {
175 Self { inner }
176 }
177}
178
179impl<I> Interceptor for ExecutionInterceptorAdapter<I>
180where
181 I: ExecutionInterceptor,
182{
183 fn before(&self, context: ExecutionContext) -> BoxFuture<'static, Result<()>> {
184 self.inner.before(context)
185 }
186
187 fn after(
188 &self,
189 context: ExecutionContext,
190 response: BootResponse,
191 ) -> BoxFuture<'static, Result<BootResponse>> {
192 let future = self.inner.after(context);
193 Box::pin(async move {
194 future.await?;
195 Ok(response)
196 })
197 }
198}