Skip to content

Commit 84ba0b7

Browse files
committed
Update to pass tests
Still has several major missing features, but is back to passing CI.
1 parent f0f2342 commit 84ba0b7

19 files changed

+207
-112
lines changed

core/codegen/src/derive/responder.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream {
5151
fn set_header_tokens<T: ToTokens + Spanned>(item: T) -> TokenStream {
5252
quote_spanned!(item.span() => __res.set_header(#item);)
5353
}
54-
54+
5555
let error_outcome = match fields.parent {
5656
FieldParent::Variant(p) => {
5757
// let name = p.parent.ident.append("Error");
@@ -144,7 +144,8 @@ pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream {
144144
// let variants = item.variants().map(|d| {
145145
// let var_name = &d.ident;
146146
// let (old, ty) = d.fields().iter().next().map(|f| {
147-
// let ty = f.ty.with_replaced_lifetimes(Lifetime::new("'o", Span::call_site()));
147+
// let ty = f.ty.with_replaced_lifetimes(
148+
// Lifetime::new("'o", Span::call_site()));
148149
// (f.ty.clone(), ty)
149150
// }).expect("have at least one field");
150151
// let output_life = if old == ty {
@@ -170,7 +171,9 @@ pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream {
170171
// .map(|p| &p.ident)
171172
// .filter(|p| generic_used(p, &response_types))
172173
// .collect();
173-
// // let bounds: Vec<_> = item.variants().map(|f| bounds_from_fields(f.fields()).expect("Bounds must be valid")).collect();
174+
// let bounds: Vec<_> = item.variants()
175+
// .map(|f| bounds_from_fields(f.fields()).expect("Bounds must be valid"))
176+
// .collect();
174177
// let bounds: Vec<_> = item.variants()
175178
// .flat_map(|f| responder_types(f.fields()).into_iter())
176179
// .map(|t| quote!{#t: #_response::Responder<'r, 'o>,})
@@ -189,14 +192,16 @@ pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream {
189192
// // TODO: validate this impl - roughly each variant must be (at least) inv
190193
// // wrt a lifetime, since they impl CanTransendTo<Inv<'r>>
191194
// // TODO: also need to add requirements on the type parameters
192-
// unsafe impl<'r, 'o: 'r, #(#type_params: 'r,)*> ::rocket::catcher::Transient for #name<'r, 'o, #(#type_params,)*>
193-
// where #(#bounds)*
195+
// unsafe impl<'r, 'o: 'r, #(#type_params: 'r,)*> ::rocket::catcher::Transient
196+
// for #name<'r, 'o, #(#type_params,)*>
197+
// where #(#bounds)*
194198
// {
195199
// type Static = #name<'static, 'static>;
196200
// type Transience = ::rocket::catcher::Inv<'r>;
197201
// }
198-
// impl<'r, 'o: 'r, #(#type_params,)*> #TypedError<'r> for #name<'r, 'o, #(#type_params,)*>
199-
// where #(#bounds)*
202+
// impl<'r, 'o: 'r, #(#type_params,)*> #TypedError<'r>
203+
// for #name<'r, 'o, #(#type_params,)*>
204+
// where #(#bounds)*
200205
// {
201206
// fn source(&self) -> #_Option<&dyn #TypedError<'r>> {
202207
// match self {

core/lib/src/catcher/catcher.rs

+16-16
Original file line numberDiff line numberDiff line change
@@ -145,26 +145,26 @@ impl Catcher {
145145
///
146146
/// ```rust
147147
/// use rocket::request::Request;
148-
/// use rocket::catcher::{Catcher, BoxFuture, ErasedError};
148+
/// use rocket::catcher::{Catcher, BoxFuture, TypedError};
149149
/// use rocket::response::Responder;
150150
/// use rocket::http::Status;
151151
///
152-
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: ErasedError<'r>)
152+
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: Option<&'r dyn TypedError<'r>>)
153153
/// -> BoxFuture<'r>
154154
/// {
155155
/// let res = (status, format!("404: {}", req.uri()));
156-
/// Box::pin(async move { res.respond_to(req).map_err(|s| (s, _e)) })
156+
/// Box::pin(async move { res.respond_to(req).responder_error() })
157157
/// }
158158
///
159-
/// fn handle_500<'r>(_: Status, req: &'r Request<'_>, _e: ErasedError<'r>) -> BoxFuture<'r> {
160-
/// Box::pin(async move{ "Whoops, we messed up!".respond_to(req).map_err(|s| (s, _e)) })
159+
/// fn handle_500<'r>(_: Status, req: &'r Request<'_>, _e: Option<&'r dyn TypedError<'r>>) -> BoxFuture<'r> {
160+
/// Box::pin(async move{ "Whoops, we messed up!".respond_to(req).responder_error() })
161161
/// }
162162
///
163-
/// fn handle_default<'r>(status: Status, req: &'r Request<'_>, _e: ErasedError<'r>)
163+
/// fn handle_default<'r>(status: Status, req: &'r Request<'_>, _e: Option<&'r dyn TypedError<'r>>)
164164
/// -> BoxFuture<'r>
165165
/// {
166166
/// let res = (status, format!("{}: {}", status, req.uri()));
167-
/// Box::pin(async move { res.respond_to(req).map_err(|s| (s, _e)) })
167+
/// Box::pin(async move { res.respond_to(req).responder_error() })
168168
/// }
169169
///
170170
/// let not_found_catcher = Catcher::new(404, handle_404);
@@ -202,15 +202,15 @@ impl Catcher {
202202
///
203203
/// ```rust
204204
/// use rocket::request::Request;
205-
/// use rocket::catcher::{Catcher, BoxFuture, ErasedError};
205+
/// use rocket::catcher::{Catcher, BoxFuture, TypedError};
206206
/// use rocket::response::Responder;
207207
/// use rocket::http::Status;
208208
///
209-
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: ErasedError<'r>)
209+
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: Option<&'r dyn TypedError<'r>>)
210210
/// -> BoxFuture<'r>
211211
/// {
212212
/// let res = (status, format!("404: {}", req.uri()));
213-
/// Box::pin(async move { res.respond_to(req).map_err(|s| (s, _e)) })
213+
/// Box::pin(async move { res.respond_to(req).responder_error() })
214214
/// }
215215
///
216216
/// let catcher = Catcher::new(404, handle_404);
@@ -230,16 +230,16 @@ impl Catcher {
230230
///
231231
/// ```rust
232232
/// use rocket::request::Request;
233-
/// use rocket::catcher::{Catcher, BoxFuture, ErasedError};
233+
/// use rocket::catcher::{Catcher, BoxFuture, TypedError};
234234
/// use rocket::response::Responder;
235235
/// use rocket::http::Status;
236236
/// # use rocket::uri;
237237
///
238-
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: ErasedError<'r>)
238+
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: Option<&'r dyn TypedError<'r>>)
239239
/// -> BoxFuture<'r>
240240
/// {
241241
/// let res = (status, format!("404: {}", req.uri()));
242-
/// Box::pin(async move { res.respond_to(req).map_err(|s| (s, _e)) })
242+
/// Box::pin(async move { res.respond_to(req).responder_error() })
243243
/// }
244244
///
245245
/// let catcher = Catcher::new(404, handle_404);
@@ -286,15 +286,15 @@ impl Catcher {
286286
///
287287
/// ```rust
288288
/// use rocket::request::Request;
289-
/// use rocket::catcher::{Catcher, BoxFuture, ErasedError};
289+
/// use rocket::catcher::{Catcher, BoxFuture, TypedError};
290290
/// use rocket::response::Responder;
291291
/// use rocket::http::Status;
292292
///
293-
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: ErasedError<'r>)
293+
/// fn handle_404<'r>(status: Status, req: &'r Request<'_>, _e: Option<&'r dyn TypedError<'r>>)
294294
/// -> BoxFuture<'r>
295295
/// {
296296
/// let res = (status, format!("404: {}", req.uri()));
297-
/// Box::pin(async move { res.respond_to(req).map_err(|s| (s, _e)) })
297+
/// Box::pin(async move { res.respond_to(req).responder_error() })
298298
/// }
299299
///
300300
/// let catcher = Catcher::new(404, handle_404);

core/lib/src/catcher/handler.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub type BoxFuture<'r, T = Result<'r>> = futures::future::BoxFuture<'r, T>;
3030
/// and used as follows:
3131
///
3232
/// ```rust,no_run
33-
/// use rocket::{Request, Catcher, catcher::{self, ErasedError}};
33+
/// use rocket::{Request, Catcher, catcher::{self, TypedError}};
3434
/// use rocket::response::{Response, Responder};
3535
/// use rocket::http::Status;
3636
///
@@ -46,16 +46,16 @@ pub type BoxFuture<'r, T = Result<'r>> = futures::future::BoxFuture<'r, T>;
4646
///
4747
/// #[rocket::async_trait]
4848
/// impl catcher::Handler for CustomHandler {
49-
/// async fn handle<'r>(&self, status: Status, req: &'r Request<'_>, _e: ErasedError<'r>)
49+
/// async fn handle<'r>(&self, status: Status, req: &'r Request<'_>, _e: Option<&'r dyn TypedError<'r>>)
5050
/// -> catcher::Result<'r>
5151
/// {
5252
/// let inner = match self.0 {
53-
/// Kind::Simple => "simple".respond_to(req).map_err(|e| (e, _e))?,
54-
/// Kind::Intermediate => "intermediate".respond_to(req).map_err(|e| (e, _e))?,
55-
/// Kind::Complex => "complex".respond_to(req).map_err(|e| (e, _e))?,
53+
/// Kind::Simple => "simple".respond_to(req).responder_error()?,
54+
/// Kind::Intermediate => "intermediate".respond_to(req).responder_error()?,
55+
/// Kind::Complex => "complex".respond_to(req).responder_error()?,
5656
/// };
5757
///
58-
/// Response::build_from(inner).status(status).ok()
58+
/// Response::build_from(inner).status(status).ok::<()>().responder_error()
5959
/// }
6060
/// }
6161
///

core/lib/src/catcher/types.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use either::Either;
22
use transient::{Any, CanRecoverFrom, CanTranscendTo, Downcast, Transience};
3-
use crate::{http::Status, Request, Response};
3+
use crate::{http::Status, response::{self, Responder}, Request, Response};
44
#[doc(inline)]
55
pub use transient::{Static, Transient, TypeId, Inv};
66

@@ -70,6 +70,7 @@ impl<'r> TypedError<'r> for std::io::Error {
7070

7171
impl<'r> TypedError<'r> for std::num::ParseIntError {}
7272
impl<'r> TypedError<'r> for std::num::ParseFloatError {}
73+
impl<'r> TypedError<'r> for std::string::FromUtf8Error {}
7374

7475
#[cfg(feature = "json")]
7576
impl<'r> TypedError<'r> for serde_json::Error {}
@@ -79,6 +80,13 @@ impl<'r> TypedError<'r> for rmp_serde::encode::Error {}
7980
#[cfg(feature = "msgpack")]
8081
impl<'r> TypedError<'r> for rmp_serde::decode::Error {}
8182

83+
// TODO: This is a hack to make any static type implement Transient
84+
impl<'r, T: std::fmt::Debug + Send + Sync + 'static> TypedError<'r> for response::Debug<T> {
85+
fn respond_to(&self, request: &'r Request<'_>) -> Result<Response<'r>, Status> {
86+
format!("{:?}", self.0).respond_to(request).responder_error()
87+
}
88+
}
89+
8290
impl<'r, L, R> TypedError<'r> for Either<L, R>
8391
where L: TypedError<'r> + Transient,
8492
L::Transience: CanTranscendTo<Inv<'r>>,

core/lib/src/lifecycle.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ impl Rocket<Orbit> {
292292
/// of times)
293293
/// - The longest path base
294294
/// - Matching status
295+
/// - The error's built-in responder (TODO: should this be before untyped catchers?)
295296
/// - If no catcher is found, Rocket's default handler is invoked
296297
///
297298
/// Return `Ok(result)` if the handler succeeded. Returns `Ok(Some(Status))`
@@ -308,24 +309,29 @@ impl Rocket<Orbit> {
308309
// Only go up to 5 levels deep (to prevent an endless cycle)
309310
.take(5)
310311
// Map to catchers
311-
.filter_map(|e| self.router.catch(status, req, Some(e.trait_obj_typeid())).map(|c| (c, e)))
312+
.filter_map(|e| {
313+
self.router.catch(status, req, Some(e.trait_obj_typeid())).map(|c| (c, e))
314+
})
312315
// Select the minimum by the catcher's rank
313316
.min_by_key(|(c, _)| c.rank);
314317
if let Some((catcher, e)) = catchers {
315318
self.invoke_specific_catcher(catcher, status, Some(e), req).await
316319
} else if let Some(catcher) = self.router.catch(status, req, None) {
317320
self.invoke_specific_catcher(catcher, status, error, req).await
321+
} else if let Some(res) = error.and_then(|e| e.respond_to(req).ok()) {
322+
Ok(res)
318323
} else {
319324
info!(name: "catcher", name = "rocket::default", "uri.base" = "/", code = status.code,
320325
"no registered catcher: using Rocket default");
321326
Ok(catcher::default_handler(status, req))
322327
}
328+
// TODO: Clean this up
323329
// let items = std::iter::from_fn(|| {
324330
// let tmp = error.map(|e| self.router.catch(status, req, Some(e.trait_obj_typeid())));
325331
// error_copy = error.and_then(|e| e.source());
326332
// tmp
327333
// }).take(5).filter_map(|e| e).min_by_key(|e| e.rank);
328-
334+
329335
// let mut error_copy = error;
330336
// let mut counter = 0;
331337
// // Matches error [.source ...] type

core/lib/src/outcome.rs

+13
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
//! a type of `Option<S>`. If an `Outcome` is a `Forward`, the `Option` will be
8787
//! `None`.
8888
89+
use crate::catcher::TypedError;
8990
use crate::request;
9091
use crate::data::{self, Data, FromData};
9192
use crate::http::Status;
@@ -634,7 +635,19 @@ impl<S, F> Outcome<S, std::convert::Infallible, F> {
634635
Self::Error(e) => match e {},
635636
}
636637
}
638+
}
637639

640+
impl<'r, S, E: TypedError<'r>> Outcome<S, E, Status> {
641+
/// Convenience function to convert the error type from `Infallible`
642+
/// to any other type. This is trivially possible, since `Infallible`
643+
/// cannot be constructed, so this cannot be an Error variant
644+
pub fn responder_error(self) -> Result<S, Status> {
645+
match self {
646+
Self::Success(v) => Ok(v),
647+
Self::Forward(v) => Err(v),
648+
Self::Error(e) => Err(e.status()),
649+
}
650+
}
638651
}
639652

640653
impl<'a, S: Send + 'a, E: Send + 'a, F: Send + 'a> Outcome<S, E, F> {

core/lib/src/response/debug.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use transient::Static;
2+
13
use crate::request::Request;
24
use crate::response::{self, Responder};
35
use crate::http::Status;
@@ -29,6 +31,7 @@ use crate::http::Status;
2931
/// Because of the generic `From<E>` implementation for `Debug<E>`, conversions
3032
/// from `Result<T, E>` to `Result<T, Debug<E>>` through `?` occur
3133
/// automatically:
34+
/// TODO: this has changed
3235
///
3336
/// ```rust
3437
/// use std::string::FromUtf8Error;
@@ -37,7 +40,7 @@ use crate::http::Status;
3740
/// use rocket::response::Debug;
3841
///
3942
/// #[get("/")]
40-
/// fn rand_str() -> Result<String, Debug<FromUtf8Error>> {
43+
/// fn rand_str() -> Result<String, FromUtf8Error> {
4144
/// # /*
4245
/// let bytes: Vec<u8> = random_bytes();
4346
/// # */
@@ -56,17 +59,19 @@ use crate::http::Status;
5659
/// use rocket::response::Debug;
5760
///
5861
/// #[get("/")]
59-
/// fn rand_str() -> Result<String, Debug<FromUtf8Error>> {
62+
/// fn rand_str() -> Result<String, FromUtf8Error> {
6063
/// # /*
6164
/// let bytes: Vec<u8> = random_bytes();
6265
/// # */
6366
/// # let bytes: Vec<u8> = vec![];
64-
/// String::from_utf8(bytes).map_err(Debug)
67+
/// String::from_utf8(bytes)
6568
/// }
6669
/// ```
6770
#[derive(Debug)]
6871
pub struct Debug<E>(pub E);
6972

73+
impl<E: 'static> Static for Debug<E> {}
74+
7075
impl<E> From<E> for Debug<E> {
7176
#[inline(always)]
7277
fn from(e: E) -> Self {

core/lib/src/response/flash.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,14 @@ const FLASH_COOKIE_DELIM: char = ':';
5050
/// # #[macro_use] extern crate rocket;
5151
/// use rocket::response::{Flash, Redirect};
5252
/// use rocket::request::FlashMessage;
53+
/// use rocket::either::Either;
5354
///
5455
/// #[post("/login/<name>")]
55-
/// fn login(name: &str) -> Result<&'static str, Flash<Redirect>> {
56+
/// fn login(name: &str) -> Either<&'static str, Flash<Redirect>> {
5657
/// if name == "special_user" {
57-
/// Ok("Hello, special user!")
58+
/// Either::Left("Hello, special user!")
5859
/// } else {
59-
/// Err(Flash::error(Redirect::to(uri!(index)), "Invalid username."))
60+
/// Either::Right(Flash::error(Redirect::to(uri!(index)), "Invalid username."))
6061
/// }
6162
/// }
6263
///

0 commit comments

Comments
 (0)