a3s_boot/pipeline/
filter.rs1use super::ExecutionContext;
2use crate::{
3 BootError, BootErrorKind, BootResponse, BoxFuture, Result, TransportContext, TransportReply,
4 WebSocketContext, WebSocketMessage,
5};
6
7pub trait ExceptionFilter: Send + Sync + 'static {
9 fn catch(
10 &self,
11 context: ExecutionContext,
12 error: BootError,
13 ) -> BoxFuture<'static, Result<Option<BootResponse>>>;
14}
15
16#[derive(Debug, Clone, PartialEq)]
18pub struct TransportExceptionResponse {
19 reply: Option<TransportReply>,
20}
21
22impl TransportExceptionResponse {
23 pub fn reply(reply: TransportReply) -> Self {
24 Self { reply: Some(reply) }
25 }
26
27 pub fn empty() -> Self {
28 Self { reply: None }
29 }
30
31 pub fn into_reply(self) -> Option<TransportReply> {
32 self.reply
33 }
34}
35
36impl From<TransportReply> for TransportExceptionResponse {
37 fn from(reply: TransportReply) -> Self {
38 Self::reply(reply)
39 }
40}
41
42pub trait TransportExceptionFilter: Send + Sync + 'static {
44 fn catch(
45 &self,
46 context: TransportContext,
47 error: BootError,
48 ) -> BoxFuture<'static, Result<Option<TransportExceptionResponse>>>;
49}
50
51impl<F, Fut> TransportExceptionFilter for F
52where
53 F: Fn(TransportContext, BootError) -> Fut + Send + Sync + 'static,
54 Fut: std::future::Future<Output = Result<Option<TransportExceptionResponse>>> + Send + 'static,
55{
56 fn catch(
57 &self,
58 context: TransportContext,
59 error: BootError,
60 ) -> BoxFuture<'static, Result<Option<TransportExceptionResponse>>> {
61 Box::pin(self(context, error))
62 }
63}
64
65#[derive(Debug, Clone, PartialEq)]
67pub struct WebSocketExceptionResponse {
68 message: Option<WebSocketMessage>,
69}
70
71impl WebSocketExceptionResponse {
72 pub fn message(message: WebSocketMessage) -> Self {
73 Self {
74 message: Some(message),
75 }
76 }
77
78 pub fn empty() -> Self {
79 Self { message: None }
80 }
81
82 pub fn into_message(self) -> Option<WebSocketMessage> {
83 self.message
84 }
85}
86
87impl From<WebSocketMessage> for WebSocketExceptionResponse {
88 fn from(message: WebSocketMessage) -> Self {
89 Self::message(message)
90 }
91}
92
93pub trait WebSocketExceptionFilter: Send + Sync + 'static {
95 fn catch(
96 &self,
97 context: WebSocketContext,
98 error: BootError,
99 ) -> BoxFuture<'static, Result<Option<WebSocketExceptionResponse>>>;
100}
101
102impl<F, Fut> WebSocketExceptionFilter for F
103where
104 F: Fn(WebSocketContext, BootError) -> Fut + Send + Sync + 'static,
105 Fut: std::future::Future<Output = Result<Option<WebSocketExceptionResponse>>> + Send + 'static,
106{
107 fn catch(
108 &self,
109 context: WebSocketContext,
110 error: BootError,
111 ) -> BoxFuture<'static, Result<Option<WebSocketExceptionResponse>>> {
112 Box::pin(self(context, error))
113 }
114}
115
116impl<F, Fut> ExceptionFilter for F
117where
118 F: Fn(ExecutionContext, BootError) -> Fut + Send + Sync + 'static,
119 Fut: std::future::Future<Output = Result<Option<BootResponse>>> + Send + 'static,
120{
121 fn catch(
122 &self,
123 context: ExecutionContext,
124 error: BootError,
125 ) -> BoxFuture<'static, Result<Option<BootResponse>>> {
126 Box::pin(self(context, error))
127 }
128}
129
130pub struct CatchFilter<F> {
132 kinds: Vec<BootErrorKind>,
133 filter: F,
134}
135
136impl<F> CatchFilter<F> {
137 pub fn new<I>(kinds: I, filter: F) -> Self
138 where
139 I: IntoIterator<Item = BootErrorKind>,
140 {
141 Self {
142 kinds: kinds.into_iter().collect(),
143 filter,
144 }
145 }
146
147 pub fn kinds(&self) -> &[BootErrorKind] {
148 &self.kinds
149 }
150
151 pub fn into_inner(self) -> F {
152 self.filter
153 }
154}
155
156impl<F> ExceptionFilter for CatchFilter<F>
157where
158 F: ExceptionFilter,
159{
160 fn catch(
161 &self,
162 context: ExecutionContext,
163 error: BootError,
164 ) -> BoxFuture<'static, Result<Option<BootResponse>>> {
165 if self.kinds.is_empty() || self.kinds.contains(&error.kind()) {
166 return self.filter.catch(context, error);
167 }
168
169 Box::pin(async { Ok(None) })
170 }
171}
172
173impl<F> TransportExceptionFilter for CatchFilter<F>
174where
175 F: TransportExceptionFilter,
176{
177 fn catch(
178 &self,
179 context: TransportContext,
180 error: BootError,
181 ) -> BoxFuture<'static, Result<Option<TransportExceptionResponse>>> {
182 if self.kinds.is_empty() || self.kinds.contains(&error.kind()) {
183 return self.filter.catch(context, error);
184 }
185
186 Box::pin(async { Ok(None) })
187 }
188}
189
190impl<F> WebSocketExceptionFilter for CatchFilter<F>
191where
192 F: WebSocketExceptionFilter,
193{
194 fn catch(
195 &self,
196 context: WebSocketContext,
197 error: BootError,
198 ) -> BoxFuture<'static, Result<Option<WebSocketExceptionResponse>>> {
199 if self.kinds.is_empty() || self.kinds.contains(&error.kind()) {
200 return self.filter.catch(context, error);
201 }
202
203 Box::pin(async { Ok(None) })
204 }
205}
206
207pub fn catch_errors<I, F>(kinds: I, filter: F) -> CatchFilter<F>
209where
210 I: IntoIterator<Item = BootErrorKind>,
211{
212 CatchFilter::new(kinds, filter)
213}