1
1
use std:: fmt;
2
2
use std:: io:: Cursor ;
3
3
4
+ use transient:: TypeId ;
5
+
4
6
use crate :: http:: uri:: Path ;
5
7
use crate :: http:: ext:: IntoOwned ;
6
8
use crate :: response:: Response ;
@@ -122,6 +124,9 @@ pub struct Catcher {
122
124
/// The catcher's associated error handler.
123
125
pub handler : Box < dyn Handler > ,
124
126
127
+ /// Catcher error type
128
+ pub ( crate ) error_type : Option < ( TypeId , & ' static str ) > ,
129
+
125
130
/// The mount point.
126
131
pub ( crate ) base : uri:: Origin < ' static > ,
127
132
@@ -134,10 +139,11 @@ pub struct Catcher {
134
139
pub ( crate ) location : Option < ( & ' static str , u32 , u32 ) > ,
135
140
}
136
141
137
- // The rank is computed as -(number of nonempty segments in base) => catchers
142
+ // The rank is computed as -(number of nonempty segments in base) *2 => catchers
138
143
// with more nonempty segments have lower ranks => higher precedence.
144
+ // Doubled to provide space between for typed catchers.
139
145
fn rank ( base : Path < ' _ > ) -> isize {
140
- -( base. segments ( ) . filter ( |s| !s. is_empty ( ) ) . count ( ) as isize )
146
+ -( base. segments ( ) . filter ( |s| !s. is_empty ( ) ) . count ( ) as isize ) * 2
141
147
}
142
148
143
149
impl Catcher {
@@ -149,22 +155,26 @@ impl Catcher {
149
155
///
150
156
/// ```rust
151
157
/// use rocket::request::Request;
152
- /// use rocket::catcher::{Catcher, BoxFuture, ErasedErrorRef };
158
+ /// use rocket::catcher::{Catcher, BoxFuture, ErasedError };
153
159
/// use rocket::response::Responder;
154
160
/// use rocket::http::Status;
155
161
///
156
- /// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
162
+ /// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: ErasedError<'r>)
163
+ /// -> BoxFuture<'r>
164
+ /// {
157
165
/// let res = (status, format!("404: {}", req.uri()));
158
- /// Box::pin(async move { res.respond_to(req) })
166
+ /// Box::pin(async move { res.respond_to(req).map_err(|s| (s, _e)) })
159
167
/// }
160
168
///
161
- /// fn handle_500<'r>(_: Status, req: &'r Request<'_>, _e: &ErasedErrorRef <'r>) -> BoxFuture<'r> {
162
- /// Box::pin(async move{ "Whoops, we messed up!".respond_to(req) })
169
+ /// fn handle_500<'r>(_: Status, req: &'r Request<'_>, _e: ErasedError <'r>) -> BoxFuture<'r> {
170
+ /// Box::pin(async move{ "Whoops, we messed up!".respond_to(req).map_err(|s| (s, _e)) })
163
171
/// }
164
172
///
165
- /// fn handle_default<'r>(status: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
173
+ /// fn handle_default<'r>(status: Status, req: &'r Request<'_>, _e: ErasedError<'r>)
174
+ /// -> BoxFuture<'r>
175
+ /// {
166
176
/// let res = (status, format!("{}: {}", status, req.uri()));
167
- /// Box::pin(async move { res.respond_to(req) })
177
+ /// Box::pin(async move { res.respond_to(req).map_err(|s| (s, _e)) })
168
178
/// }
169
179
///
170
180
/// let not_found_catcher = Catcher::new(404, handle_404);
@@ -189,6 +199,7 @@ impl Catcher {
189
199
name : None ,
190
200
base : uri:: Origin :: root ( ) . clone ( ) ,
191
201
handler : Box :: new ( handler) ,
202
+ error_type : None ,
192
203
rank : rank ( uri:: Origin :: root ( ) . path ( ) ) ,
193
204
code,
194
205
location : None ,
@@ -201,13 +212,15 @@ impl Catcher {
201
212
///
202
213
/// ```rust
203
214
/// use rocket::request::Request;
204
- /// use rocket::catcher::{Catcher, BoxFuture, ErasedErrorRef };
215
+ /// use rocket::catcher::{Catcher, BoxFuture, ErasedError };
205
216
/// use rocket::response::Responder;
206
217
/// use rocket::http::Status;
207
218
///
208
- /// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
219
+ /// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: ErasedError<'r>)
220
+ /// -> BoxFuture<'r>
221
+ /// {
209
222
/// let res = (status, format!("404: {}", req.uri()));
210
- /// Box::pin(async move { res.respond_to(req) })
223
+ /// Box::pin(async move { res.respond_to(req).map_err(|s| (s, _e)) })
211
224
/// }
212
225
///
213
226
/// let catcher = Catcher::new(404, handle_404);
@@ -227,14 +240,16 @@ impl Catcher {
227
240
///
228
241
/// ```rust
229
242
/// use rocket::request::Request;
230
- /// use rocket::catcher::{Catcher, BoxFuture, ErasedErrorRef };
243
+ /// use rocket::catcher::{Catcher, BoxFuture, ErasedError };
231
244
/// use rocket::response::Responder;
232
245
/// use rocket::http::Status;
233
246
/// # use rocket::uri;
234
247
///
235
- /// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
248
+ /// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: ErasedError<'r>)
249
+ /// -> BoxFuture<'r>
250
+ /// {
236
251
/// let res = (status, format!("404: {}", req.uri()));
237
- /// Box::pin(async move { res.respond_to(req) })
252
+ /// Box::pin(async move { res.respond_to(req).map_err(|s| (s, _e)) })
238
253
/// }
239
254
///
240
255
/// let catcher = Catcher::new(404, handle_404);
@@ -281,13 +296,15 @@ impl Catcher {
281
296
///
282
297
/// ```rust
283
298
/// use rocket::request::Request;
284
- /// use rocket::catcher::{Catcher, BoxFuture, ErasedErrorRef };
299
+ /// use rocket::catcher::{Catcher, BoxFuture, ErasedError };
285
300
/// use rocket::response::Responder;
286
301
/// use rocket::http::Status;
287
302
///
288
- /// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: &ErasedErrorRef<'r>) -> BoxFuture<'r> {
303
+ /// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: ErasedError<'r>)
304
+ /// -> BoxFuture<'r>
305
+ /// {
289
306
/// let res = (status, format!("404: {}", req.uri()));
290
- /// Box::pin(async move { res.respond_to(req) })
307
+ /// Box::pin(async move { res.respond_to(req).map_err(|s| (s, _e)) })
291
308
/// }
292
309
///
293
310
/// let catcher = Catcher::new(404, handle_404);
@@ -332,6 +349,8 @@ pub struct StaticInfo {
332
349
pub name : & ' static str ,
333
350
/// The catcher's status code.
334
351
pub code : Option < u16 > ,
352
+ /// The catcher's error type.
353
+ pub error_type : Option < ( TypeId , & ' static str ) > ,
335
354
/// The catcher's handler, i.e, the annotated function.
336
355
pub handler : for <' r > fn ( Status , & ' r Request < ' _ > , ErasedError < ' r > ) -> BoxFuture < ' r > ,
337
356
/// The file, line, and column where the catcher was defined.
@@ -343,7 +362,13 @@ impl From<StaticInfo> for Catcher {
343
362
#[ inline]
344
363
fn from ( info : StaticInfo ) -> Catcher {
345
364
let mut catcher = Catcher :: new ( info. code , info. handler ) ;
365
+ if info. error_type . is_some ( ) {
366
+ // Lower rank if the error_type is defined, to ensure typed catchers
367
+ // are always tried first
368
+ catcher. rank -= 1 ;
369
+ }
346
370
catcher. name = Some ( info. name . into ( ) ) ;
371
+ catcher. error_type = info. error_type ;
347
372
catcher. location = Some ( info. location ) ;
348
373
catcher
349
374
}
@@ -354,6 +379,7 @@ impl fmt::Debug for Catcher {
354
379
f. debug_struct ( "Catcher" )
355
380
. field ( "name" , & self . name )
356
381
. field ( "base" , & self . base )
382
+ . field ( "error_type" , & self . error_type . as_ref ( ) . map ( |( _, n) | n) )
357
383
. field ( "code" , & self . code )
358
384
. field ( "rank" , & self . rank )
359
385
. finish ( )
0 commit comments