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

View File

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

View File

@ -10,11 +10,13 @@ use data::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> {
type Input = Status;
#[inline]
fn into_outcome(self) -> Outcome<S, E> {
fn into_outcome(self, status: Status) -> Outcome<S, E> {
match self {
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.
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> {
@ -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>`.
///
/// ```rust

View File

@ -12,10 +12,13 @@ use http::uri::URI;
pub type Outcome<S, E> = outcome::Outcome<S, (Status, 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 {
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);
}
r.into_outcome()
r.into_outcome(Status::BadRequest)
}
}