diff --git a/contrib/codegen/src/database.rs b/contrib/codegen/src/database.rs index 45646951..be5a2c13 100644 --- a/contrib/codegen/src/database.rs +++ b/contrib/codegen/src/database.rs @@ -145,7 +145,7 @@ pub fn database_attr(attr: TokenStream, input: TokenStream) -> Result) -> #request::Outcome { use ::rocket::{Outcome, http::Status}; - let pool = request.guard::<::rocket::State<#pool_type>>()?; + let pool = ::rocket::try_outcome!(request.guard::<::rocket::State<#pool_type>>()); match pool.0.get() { Ok(conn) => Outcome::Success(#guard_type(conn)), diff --git a/contrib/lib/src/json.rs b/contrib/lib/src/json.rs index 2a1be382..a59948ca 100644 --- a/contrib/lib/src/json.rs +++ b/contrib/lib/src/json.rs @@ -143,7 +143,7 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for Json { } fn from_data(_: &Request<'_>, o: Transformed<'a, Self>) -> Outcome { - let string = o.borrowed()?; + let string = try_outcome!(o.borrowed()); match serde_json::from_str(&string) { Ok(v) => Success(Json(v)), Err(e) => { diff --git a/contrib/lib/src/msgpack.rs b/contrib/lib/src/msgpack.rs index 354cd1da..13f91043 100644 --- a/contrib/lib/src/msgpack.rs +++ b/contrib/lib/src/msgpack.rs @@ -131,7 +131,7 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for MsgPack { fn from_data(_: &Request<'_>, o: Transformed<'a, Self>) -> Outcome { use self::Error::*; - let buf = o.borrowed()?; + let buf = try_outcome!(o.borrowed()); match rmp_serde::from_slice(&buf) { Ok(val) => Success(MsgPack(val)), Err(e) => { diff --git a/core/lib/src/data/from_data.rs b/core/lib/src/data/from_data.rs index 3aa5779b..db17bbb2 100644 --- a/core/lib/src/data/from_data.rs +++ b/core/lib/src/data/from_data.rs @@ -219,7 +219,7 @@ pub type Transformed<'a, T> = /// // Retrieve a borrow to the now transformed `String` (an &str). This /// // is only correct because we know we _always_ return a `Borrowed` from /// // `transform` above. -/// let string = outcome.borrowed()?; +/// let string = try_outcome!(outcome.borrowed()); /// /// // Perform a crude, inefficient parse. /// let splits: Vec<&str> = string.split(" ").collect(); @@ -370,16 +370,17 @@ pub trait FromData<'a>: Sized { /// following: /// /// ```rust + /// # #[macro_use] extern crate rocket; /// # use rocket::data::{Data, FromData, Transformed, Outcome}; /// # fn f<'a>(outcome: Transformed<'a, Data>) -> Outcome>::Error> { /// // If `Owned` was returned from `transform`: - /// let data = outcome.owned()?; + /// let data = try_outcome!(outcome.owned()); /// # unimplemented!() /// # } /// /// # fn g<'a>(outcome: Transformed<'a, Data>) -> Outcome>::Error> { /// // If `Borrowed` was returned from `transform`: - /// let data = outcome.borrowed()?; + /// let data = try_outcome!(outcome.borrowed()); /// # unimplemented!() /// # } /// ``` @@ -399,7 +400,7 @@ impl<'f> FromData<'f> for Data { #[inline(always)] fn from_data(_: &Request<'_>, outcome: Transformed<'f, Self>) -> Outcome { - Success(outcome.owned()?) + outcome.owned() } } @@ -517,7 +518,7 @@ impl<'a, T: FromDataSimple> FromData<'a> for T { #[inline(always)] fn from_data(req: &Request<'_>, o: Transformed<'a, Self>) -> Outcome { - T::from_data(req, o.owned()?) + T::from_data(req, try_outcome!(o.owned())) } } diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index 43b7214f..87e23948 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(try_trait)] #![feature(proc_macro_hygiene)] #![feature(crate_visibility_modifier)] @@ -95,16 +94,19 @@ //! [testing chapter of the guide]: https://rocket.rs/v0.5/guide/testing/#testing #[allow(unused_imports)] #[macro_use] extern crate rocket_codegen; +// FIXME(rustdoc): We should be able to doc(inline) and not doc the +// rocket_codegen crate at all. Alas, doc-inlining will currently 1) show hidden +// proc-macros, and 2) result in proc-macros pointing to the wrong docs. #[doc(hidden)] pub use rocket_codegen::*; #[macro_use] extern crate log; #[macro_use] extern crate pear; #[doc(hidden)] #[macro_use] pub mod logger; +#[macro_use] pub mod outcome; pub mod local; pub mod request; pub mod response; -pub mod outcome; pub mod config; pub mod data; pub mod handler; diff --git a/core/lib/src/outcome.rs b/core/lib/src/outcome.rs index bbb54370..5d0332c9 100644 --- a/core/lib/src/outcome.rs +++ b/core/lib/src/outcome.rs @@ -79,7 +79,6 @@ //! `None`. use std::fmt; -use std::ops::Try; use yansi::{Paint, Color}; @@ -601,28 +600,68 @@ impl Outcome { } } -impl Try for Outcome { - type Ok = S; - type Error = Result; - - fn into_result(self) -> Result { - match self { - Success(val) => Ok(val), - Forward(val) => Err(Ok(val)), - Failure(val) => Err(Err(val)), - } - } - - fn from_error(val: Self::Error) -> Self { - match val { - Ok(val) => Forward(val), - Err(val) => Failure(val), - } - } - - fn from_ok(val: Self::Ok) -> Self { - Success(val) - } +/// Unwraps an [`Outcome`] to its success value, otherwise propagating the +/// forward or failure. +/// +/// In the case of a `Forward` or `Failure` variant, the inner type is passed to +/// [`From`](std::convert::From), allowing for the conversion between specific +/// and more general types. The resulting forward/error is immediately returned. +/// +/// Because of the early return, `try_outcome!` can only be used in methods that +/// return [`Outcome`]. +/// +/// ## Example +/// +/// ```rust,no_run +/// # #![feature(proc_macro_hygiene)] +/// # #[macro_use] extern crate rocket; +/// # use std::sync::atomic::{AtomicUsize, Ordering}; +/// use rocket::request::{self, Request, FromRequest, State}; +/// use rocket::outcome::Outcome::*; +/// +/// #[derive(Default)] +/// struct Atomics { +/// uncached: AtomicUsize, +/// cached: AtomicUsize, +/// } +/// +/// struct Guard1; +/// struct Guard2; +/// +/// impl<'a, 'r> FromRequest<'a, 'r> for Guard1 { +/// type Error = (); +/// +/// fn from_request(req: &'a Request<'r>) -> request::Outcome { +/// // Attempt to fetch the guard, passing through any error or forward. +/// let atomics = try_outcome!(req.guard::>()); +/// atomics.uncached.fetch_add(1, Ordering::Relaxed); +/// req.local_cache(|| atomics.cached.fetch_add(1, Ordering::Relaxed)); +/// +/// Success(Guard1) +/// } +/// } +/// +/// impl<'a, 'r> FromRequest<'a, 'r> for Guard2 { +/// type Error = (); +/// +/// fn from_request(req: &'a Request<'r>) -> request::Outcome { +/// // Attempt to fetch the guard, passing through any error or forward. +/// let guard1: Guard1 = try_outcome!(req.guard::()); +/// Success(Guard2) +/// } +/// } +/// ``` +#[macro_export] +macro_rules! try_outcome { + ($expr:expr $(,)?) => (match $expr { + $crate::outcome::Outcome::Success(val) => val, + $crate::outcome::Outcome::Failure(e) => { + return $crate::outcome::Outcome::Failure(::std::convert::From::from(e)) + }, + $crate::outcome::Outcome::Forward(f) => { + return $crate::outcome::Outcome::Forward(::std::convert::From::from(f)) + }, + }); } impl fmt::Debug for Outcome { diff --git a/core/lib/src/request/form/form.rs b/core/lib/src/request/form/form.rs index be4efc41..06c8f710 100644 --- a/core/lib/src/request/form/form.rs +++ b/core/lib/src/request/form/form.rs @@ -211,7 +211,7 @@ impl<'f, T: FromForm<'f>> FromData<'f> for Form { } fn from_data(_: &Request<'_>, o: Transformed<'f, Self>) -> Outcome { - >::from_data(o.borrowed()?, true).map(Form) + >::from_data(try_outcome!(o.borrowed()), true).map(Form) } } diff --git a/core/lib/src/request/form/lenient.rs b/core/lib/src/request/form/lenient.rs index e7756ec4..0cfe122d 100644 --- a/core/lib/src/request/form/lenient.rs +++ b/core/lib/src/request/form/lenient.rs @@ -105,7 +105,7 @@ impl<'f, T: FromForm<'f>> FromData<'f> for LenientForm { } fn from_data(_: &Request<'_>, o: Transformed<'f, Self>) -> Outcome { - >::from_data(o.borrowed()?, false).map(LenientForm) + >::from_data(try_outcome!(o.borrowed()), false).map(LenientForm) } } diff --git a/core/lib/src/request/from_request.rs b/core/lib/src/request/from_request.rs index dfcfe7ca..8f603487 100644 --- a/core/lib/src/request/from_request.rs +++ b/core/lib/src/request/from_request.rs @@ -245,7 +245,7 @@ impl IntoOutcome for Result { /// type Error = (); /// /// fn from_request(request: &Request<'_>) -> request::Outcome { -/// let db = request.guard::()?; +/// let db = try_outcome!(request.guard::()); /// request.cookies() /// .get_private("user_id") /// .and_then(|cookie| cookie.value().parse().ok()) @@ -259,7 +259,7 @@ impl IntoOutcome for Result { /// /// fn from_request(request: &Request<'_>) -> request::Outcome { /// // This will unconditionally query the database! -/// let user = request.guard::()?; +/// let user = try_outcome!(request.guard::()); /// /// if user.is_admin { /// Outcome::Success(Admin { user }) @@ -326,7 +326,7 @@ impl IntoOutcome for Result { /// type Error = std::convert::Infallible; /// /// fn from_request(request: &'a Request<'_>) -> request::Outcome { -/// let user = request.guard::<&User>()?; +/// let user = try_outcome!(request.guard::<&User>()); /// /// if user.is_admin { /// Outcome::Success(Admin { user }) diff --git a/examples/manual_routes/src/main.rs b/examples/manual_routes/src/main.rs index 18fc8746..9d542663 100644 --- a/examples/manual_routes/src/main.rs +++ b/examples/manual_routes/src/main.rs @@ -6,7 +6,7 @@ mod tests; use std::{io, env}; use std::fs::File; -use rocket::{Request, Handler, Route, Data, Catcher}; +use rocket::{Request, Handler, Route, Data, Catcher, try_outcome}; use rocket::http::{Status, RawStr}; use rocket::response::{self, Responder, status::Custom}; use rocket::handler::Outcome; @@ -30,10 +30,11 @@ fn name<'a>(req: &'a Request, _: Data) -> Outcome<'a> { } fn echo_url<'r>(req: &'r Request, _: Data) -> Outcome<'r> { - let param = req.get_param::<&RawStr>(1) + let param_outcome = req.get_param::<&RawStr>(1) .and_then(|res| res.ok()) - .into_outcome(Status::BadRequest)?; + .into_outcome(Status::BadRequest); + let param = try_outcome!(param_outcome); Outcome::try_from(req, RawStr::from_str(param).url_decode()) } @@ -79,10 +80,11 @@ impl CustomHandler { impl Handler for CustomHandler { fn handle<'r>(&self, req: &'r Request, data: Data) -> Outcome<'r> { - let id = req.get_param::<&RawStr>(0) + let id_outcome = req.get_param::<&RawStr>(0) .and_then(|res| res.ok()) - .or_forward(data)?; + .or_forward(data); + let id = try_outcome!(id_outcome); Outcome::from(req, format!("{} - {}", self.data, id)) } } diff --git a/examples/request_local_state/src/main.rs b/examples/request_local_state/src/main.rs index 8bfe071e..f2a3ccec 100644 --- a/examples/request_local_state/src/main.rs +++ b/examples/request_local_state/src/main.rs @@ -22,7 +22,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Guard1 { type Error = (); fn from_request(req: &'a Request<'r>) -> request::Outcome { - let atomics = req.guard::>()?; + let atomics = try_outcome!(req.guard::>()); atomics.uncached.fetch_add(1, Ordering::Relaxed); req.local_cache(|| atomics.cached.fetch_add(1, Ordering::Relaxed)); @@ -34,7 +34,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Guard2 { type Error = (); fn from_request(req: &'a Request<'r>) -> request::Outcome { - req.guard::()?; + try_outcome!(req.guard::()); Success(Guard2) } }