Add optional input for IntoOutcome. Add mapper methods to Outcome.

This is a breaking change to `IntoOutcome`.

The MsgPack and JSON types now use `into_outcome` to generate the final
`Outcome` from their `FromData` implementations.

Resolves #98.
This commit is contained in:
Sergio Benitez 2017-04-18 21:52:02 -07:00
parent 8555a0fad5
commit 7b48ca7103
6 changed files with 88 additions and 23 deletions

View File

@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut};
use std::io::Read; use std::io::Read;
use rocket::config; use rocket::config;
use rocket::outcome::Outcome; use rocket::outcome::{Outcome, IntoOutcome};
use rocket::request::Request; use rocket::request::Request;
use rocket::data::{self, Data, FromData}; use rocket::data::{self, Data, FromData};
use rocket::response::{self, Responder, content}; use rocket::response::{self, Responder, content};
@ -100,14 +100,10 @@ impl<T: Deserialize> FromData for JSON<T> {
.and_then(|c| c.limits.get("json")) .and_then(|c| c.limits.get("json"))
.unwrap_or(LIMIT); .unwrap_or(LIMIT);
let reader = data.open().take(size_limit); serde_json::from_reader(data.open().take(size_limit))
match serde_json::from_reader(reader).map(|val| JSON(val)) { .map(|val| JSON(val))
Ok(value) => Outcome::Success(value), .map_err(|e| { error_!("Couldn't parse JSON body: {:?}", e); e })
Err(e) => { .into_outcome(Status::BadRequest)
error_!("Couldn't parse JSON body: {:?}", e);
Outcome::Failure((Status::BadRequest, e))
}
}
} }
} }

View File

@ -4,7 +4,7 @@ use std::ops::{Deref, DerefMut};
use std::io::{Cursor, Read}; use std::io::{Cursor, Read};
use rocket::config; use rocket::config;
use rocket::outcome::Outcome; use rocket::outcome::{Outcome, IntoOutcome};
use rocket::request::Request; use rocket::request::Request;
use rocket::data::{self, Data, FromData}; use rocket::data::{self, Data, FromData};
use rocket::response::{self, Responder, Response}; use rocket::response::{self, Responder, Response};
@ -120,13 +120,9 @@ impl<T: Deserialize> FromData for MsgPack<T> {
return Outcome::Failure((Status::BadRequest, e)); return Outcome::Failure((Status::BadRequest, e));
}; };
match rmp_serde::from_slice(&buf).map(|val| MsgPack(val)) { rmp_serde::from_slice(&buf).map(|val| MsgPack(val))
Ok(value) => Outcome::Success(value), .map_err(|e| { error_!("Couldn't parse MessagePack body: {:?}", e); e })
Err(e) => { .into_outcome(Status::BadRequest)
error_!("Couldn't parse MessagePack body: {:?}", e);
Outcome::Failure((Status::BadRequest, e))
}
}
} }
} }

View File

@ -10,11 +10,13 @@ use data::Data;
pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), Data>; pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), Data>;
impl<'a, S, E> IntoOutcome<S, (Status, E), Data> for Result<S, E> { impl<'a, S, E> IntoOutcome<S, (Status, E), Data> for Result<S, E> {
type Input = Status;
#[inline] #[inline]
fn into_outcome(self) -> Outcome<S, E> { fn into_outcome(self, status: Status) -> Outcome<S, E> {
match self { match self {
Ok(val) => Success(val), Ok(val) => Success(val),
Err(err) => Failure((Status::InternalServerError, err)) Err(err) => Failure((status, err))
} }
} }
} }

View File

@ -105,7 +105,9 @@ pub enum Outcome<S, E, F> {
/// Conversion trait from some type into an Outcome type. /// Conversion trait from some type into an Outcome type.
pub trait IntoOutcome<S, E, F> { pub trait IntoOutcome<S, E, F> {
fn into_outcome(self) -> Outcome<S, E, F>; type Input: Sized;
fn into_outcome(self, input: Self::Input) -> Outcome<S, E, F>;
} }
impl<S, E, F> Outcome<S, E, F> { impl<S, E, F> Outcome<S, E, F> {
@ -329,6 +331,72 @@ impl<S, E, F> Outcome<S, E, F> {
} }
} }
/// Maps an `Outcome<S, E, F>` to an `Outcome<T, E, F>` by applying the
/// function `f` to the value of type `S` in `self` if `self` is an
/// `Outcome::Success`.
///
/// ```rust
/// # use rocket::outcome::Outcome;
/// # use rocket::outcome::Outcome::*;
/// #
/// let x: Outcome<i32, &str, usize> = Success(10);
///
/// let mapped = x.map(|v| if v == 10 { "10" } else { "not 10" });
/// assert_eq!(mapped, Success("10"));
/// ```
#[inline]
pub fn map<T, M: FnOnce(S) -> T>(self, f: M) -> Outcome<T, E, F> {
match self {
Success(val) => Success(f(val)),
Failure(val) => Failure(val),
Forward(val) => Forward(val),
}
}
/// Maps an `Outcome<S, E, F>` to an `Outcome<S, T, F>` by applying the
/// function `f` to the value of type `E` in `self` if `self` is an
/// `Outcome::Failure`.
///
/// ```rust
/// # use rocket::outcome::Outcome;
/// # use rocket::outcome::Outcome::*;
/// #
/// let x: Outcome<i32, &str, usize> = Failure("hi");
///
/// let mapped = x.map_failure(|v| if v == "hi" { 10 } else { 0 });
/// assert_eq!(mapped, Failure(10));
/// ```
#[inline]
pub fn map_failure<T, M: FnOnce(E) -> T>(self, f: M) -> Outcome<S, T, F> {
match self {
Success(val) => Success(val),
Failure(val) => Failure(f(val)),
Forward(val) => Forward(val),
}
}
/// Maps an `Outcome<S, E, F>` to an `Outcome<S, E, T>` by applying the
/// function `f` to the value of type `F` in `self` if `self` is an
/// `Outcome::Forward`.
///
/// ```rust
/// # use rocket::outcome::Outcome;
/// # use rocket::outcome::Outcome::*;
/// #
/// let x: Outcome<i32, &str, usize> = Forward(5);
///
/// let mapped = x.map_forward(|v| if v == 5 { "a" } else { "b" });
/// assert_eq!(mapped, Forward("a"));
/// ```
#[inline]
pub fn map_forward<T, M: FnOnce(F) -> T>(self, f: M) -> Outcome<S, E, T> {
match self {
Success(val) => Success(val),
Failure(val) => Failure(val),
Forward(val) => Forward(f(val)),
}
}
/// Converts from `Outcome<S, E, F>` to `Outcome<&mut S, &mut E, &mut F>`. /// Converts from `Outcome<S, E, F>` to `Outcome<&mut S, &mut E, &mut F>`.
/// ///
/// ```rust /// ```rust

View File

@ -12,10 +12,13 @@ use http::uri::URI;
pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), ()>; pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), ()>;
impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> { impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
fn into_outcome(self) -> Outcome<S, E> { type Input = Status;
#[inline]
fn into_outcome(self, status: Status) -> Outcome<S, E> {
match self { match self {
Ok(val) => Success(val), Ok(val) => Success(val),
Err(val) => Failure((Status::BadRequest, val)) Err(err) => Failure((status, err))
} }
} }
} }

View File

@ -242,6 +242,6 @@ impl<'a, 'r> FromRequest<'a, 'r> for Flash<()> {
request.cookies().remove(cookie); request.cookies().remove(cookie);
} }
r.into_outcome() r.into_outcome(Status::BadRequest)
} }
} }