Use Outcome as the result of all fallible conversions.

This commit is contained in:
Sergio Benitez 2016-10-13 18:39:23 -07:00
parent aa2f2061ac
commit 722f613686
23 changed files with 298 additions and 215 deletions

View File

@ -105,12 +105,11 @@ impl RouteGenerateExt for RouteParams {
Some(quote_stmt!(ecx,
let $name: $ty =
match ::rocket::request::FromData::from_data(&_req, _data) {
::rocket::request::DataOutcome::Success(d) => d,
::rocket::request::DataOutcome::Forward(d) =>
::rocket::outcome::Outcome::Success(d) => d,
::rocket::outcome::Outcome::Forward(d) =>
return ::rocket::Response::forward(d),
::rocket::request::DataOutcome::Failure(_) => {
let code = ::rocket::http::StatusCode::BadRequest;
return ::rocket::Response::failed(code);
::rocket::outcome::Outcome::Failure((code, _)) => {
return ::rocket::Response::failure(code);
}
};
).expect("data statement"))
@ -181,9 +180,13 @@ impl RouteGenerateExt for RouteParams {
let ty = strip_ty_lifetimes(arg.ty.clone());
fn_param_statements.push(quote_stmt!(ecx,
let $ident: $ty = match
<$ty as ::rocket::request::FromRequest>::from_request(&_req) {
Ok(v) => v,
Err(_e) => return ::rocket::Response::forward(_data)
::rocket::request::FromRequest::from_request(&_req) {
::rocket::outcome::Outcome::Success(v) => v,
::rocket::outcome::Outcome::Forward(_) =>
return ::rocket::Response::forward(_data),
::rocket::outcome::Outcome::Failure((code, _)) => {
return ::rocket::Response::failure(code)
},
};
).expect("undeclared param parsing statement"));
}
@ -240,7 +243,7 @@ fn generic_route_decorator(known_method: Option<Spanned<Method>>,
$query_statement
$data_statement
let result = $user_fn_name($fn_arguments);
::rocket::Response::complete(result)
::rocket::Response::success(result)
}
).unwrap());

View File

@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut};
use std::io::Read;
use rocket::request::{Request, Data, FromData, DataOutcome};
use rocket::response::{Responder, Outcome, ResponseOutcome, data};
use rocket::response::{Responder, ResponseOutcome, data};
use rocket::http::StatusCode;
use rocket::http::hyper::FreshHyperResponse;
@ -73,11 +73,11 @@ impl<T: Deserialize> FromData for JSON<T> {
fn from_data(request: &Request, data: Data) -> DataOutcome<Self, SerdeError> {
if !request.content_type().is_json() {
error_!("Content-Type is not JSON.");
return DataOutcome::Forward(data);
return DataOutcome::forward(data);
}
let reader = data.open().take(MAX_SIZE);
DataOutcome::from(serde_json::from_reader(reader).map(|val| JSON(val)))
DataOutcome::of(serde_json::from_reader(reader).map(|val| JSON(val)))
}
}
@ -87,7 +87,7 @@ impl<T: Serialize> Responder for JSON<T> {
Ok(json_string) => data::JSON(json_string).respond(res),
Err(e) => {
error_!("JSON failed to serialize: {:?}", e);
Outcome::Forward((StatusCode::BadRequest, res))
ResponseOutcome::forward(StatusCode::BadRequest, res)
}
}
}

View File

@ -17,7 +17,7 @@ use std::path::{Path, PathBuf};
use std::collections::HashMap;
use rocket::Rocket;
use rocket::response::{Content, Outcome, ResponseOutcome, Responder};
use rocket::response::{Content, ResponseOutcome, Responder};
use rocket::http::hyper::FreshHyperResponse;
use rocket::http::{ContentType, StatusCode};
@ -155,7 +155,7 @@ impl Responder for Template {
match self.0 {
Some(ref render) => Content(content_type, render.as_str()).respond(res),
None => Outcome::Forward((StatusCode::InternalServerError, res)),
None => ResponseOutcome::forward(StatusCode::InternalServerError, res),
}
}
}

View File

@ -4,7 +4,7 @@
extern crate rocket;
use std::fmt;
use rocket::request::{Request, FromRequest};
use rocket::request::{Request, FromRequest, RequestOutcome};
#[derive(Debug)]
struct HeaderCount(usize);
@ -17,8 +17,8 @@ impl fmt::Display for HeaderCount {
impl<'r> FromRequest<'r> for HeaderCount {
type Error = ();
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
Ok(HeaderCount(request.headers().len()))
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> {
RequestOutcome::success(HeaderCount(request.headers().len()))
}
}

View File

@ -13,35 +13,35 @@ fn forward(_req: &Request, data: Data) -> Response<'static> {
}
fn hi(_req: &Request, _: Data) -> Response<'static> {
Response::complete("Hello!")
Response::success("Hello!")
}
fn name<'a>(req: &'a Request, _: Data) -> Response<'a> {
Response::complete(req.get_param(0).unwrap_or("unnamed"))
Response::success(req.get_param(0).unwrap_or("unnamed"))
}
fn echo_url<'a>(req: &'a Request, _: Data) -> Response<'a> {
let param = req.uri().as_str().split_at(6).1;
Response::complete(String::from_param(param))
Response::success(String::from_param(param))
}
fn upload(req: &Request, data: Data) -> Response {
if !req.content_type().is_text() {
println!(" => Content-Type of upload must be data. Ignoring.");
return Response::failed(StatusCode::BadRequest);
return Response::failure(StatusCode::BadRequest);
}
let file = File::create("/tmp/upload.txt");
if let Ok(mut file) = file {
if let Ok(n) = io::copy(&mut data.open(), &mut file) {
return Response::complete(format!("OK: {} bytes uploaded.", n));
return Response::success(format!("OK: {} bytes uploaded.", n));
}
println!(" => Failed copying.");
Response::failed(StatusCode::InternalServerError)
Response::failure(StatusCode::InternalServerError)
} else {
println!(" => Couldn't open file: {:?}", file.unwrap_err());
Response::failed(StatusCode::InternalServerError)
Response::failure(StatusCode::InternalServerError)
}
}

View File

@ -7,7 +7,6 @@ authors = ["Sergio Benitez <sb@sergio.bz>"]
term-painter = "^0.2"
log = "^0.3"
url = "^1"
# mime = "^0.2"
toml = "^0.2"
[dependencies.hyper]

View File

@ -1,7 +1,6 @@
#![feature(question_mark)]
#![feature(specialization)]
#![feature(conservative_impl_trait)]
#![feature(associated_type_defaults)]
//! # Rocket - Core API Documentation
//!
@ -31,9 +30,9 @@
//! ## Usage
//!
//! The sanctioned way to use Rocket is via the code generation plugin. This
//! makes Rocket easier to use and allows a somewhat stable API as Rust matures.
//! To use Rocket in your Cargo-based project, add the following to
//! `Cargo.toml`:
//! makes Rocket easier to use and allows a somewhat stable API as Rocket
//! matures. To use Rocket with the code generation plugin in your Cargo-based
//! project, add the following to `Cargo.toml`:
//!
//! ```rust,ignore
//! [dependencies]
@ -54,7 +53,7 @@
//! ```
//!
//! See the [guide](https://guide.rocket.rs) for more information on how to
//! write Rocket application.
//! write Rocket applications.
//!
//! ## Configuration
//!
@ -65,7 +64,6 @@
extern crate term_painter;
extern crate hyper;
extern crate url;
// extern crate mime;
extern crate toml;
#[cfg(test)] #[macro_use] extern crate lazy_static;

View File

@ -1,71 +1,115 @@
use std::fmt;
use term_painter::Color::*;
use term_painter::Color;
use term_painter::ToStyle;
#[must_use]
pub enum Outcome<T> {
/// Signifies that all processing completed successfully.
Success,
/// Signifies that some processing occurred that ultimately resulted in
/// failure. As a result, no further processing can occur.
Failure,
/// Signifies that no processing occured and as such, processing can be
/// forwarded to the next available target.
Forward(T),
pub enum Outcome<S, E, F> {
/// Contains the success value.
Success(S),
/// Contains the failure error value.
Failure(E),
/// Contains the value to forward on.
Forward(F),
}
impl<T> Outcome<T> {
pub fn of<A, B: fmt::Debug>(result: Result<A, B>) -> Outcome<T> {
if let Err(e) = result {
error_!("{:?}", e);
return Outcome::Failure;
impl<S, E, F> Outcome<S, E, F> {
/// Unwraps the Outcome, yielding the contents of a Success.
///
/// # Panics
///
/// Panics if the value is not Success.
#[inline(always)]
pub fn unwrap(self) -> S {
match self {
Outcome::Success(val) => val,
_ => panic!("Expected a successful outcome!")
}
Outcome::Success
}
pub fn as_str(&self) -> &'static str {
/// Return true if this `Outcome` is a `Success`.
#[inline(always)]
pub fn is_success(&self) -> bool {
match *self {
Outcome::Success => "Success",
Outcome::Failure => "FailStop",
Outcome::Forward(..) => "Forward",
Outcome::Success(_) => true,
_ => false
}
}
fn as_int(&self) -> isize {
/// Return true if this `Outcome` is a `Failure`.
#[inline(always)]
pub fn is_failure(&self) -> bool {
match *self {
Outcome::Success => 0,
Outcome::Failure => 1,
Outcome::Forward(..) => 2,
Outcome::Failure(_) => true,
_ => false
}
}
pub fn expect_success(&self) {
if *self != Outcome::Success {
panic!("expected a successful outcome");
/// Return true if this `Outcome` is a `Forward`.
#[inline(always)]
pub fn is_forward(&self) -> bool {
match *self {
Outcome::Forward(_) => true,
_ => false
}
}
/// Converts from `Outcome<S, E, F>` to `Option<S>`.
///
/// Returns the `Some` of the `Success` if this is a `Success`, otherwise
/// returns `None`. `self` is consumed, and all other values are discarded.
#[inline(always)]
pub fn succeeded(self) -> Option<S> {
match self {
Outcome::Success(val) => Some(val),
_ => None
}
}
/// Converts from `Outcome<S, E, F>` to `Option<E>`.
///
/// Returns the `Some` of the `Failure` if this is a `Failure`, otherwise
/// returns `None`. `self` is consumed, and all other values are discarded.
#[inline(always)]
pub fn failed(self) -> Option<E> {
match self {
Outcome::Failure(val) => Some(val),
_ => None
}
}
/// Converts from `Outcome<S, E, F>` to `Option<F>`.
///
/// Returns the `Some` of the `Forward` if this is a `Forward`, otherwise
/// returns `None`. `self` is consumed, and all other values are discarded.
#[inline(always)]
pub fn forwarded(self) -> Option<F> {
match self {
Outcome::Forward(val) => Some(val),
_ => None
}
}
#[inline(always)]
fn formatting(&self) -> (Color, &'static str) {
match *self {
Outcome::Success(..) => (Green, "Succcess"),
Outcome::Failure(..) => (Red, "Failure"),
Outcome::Forward(..) => (Yellow, "Forward"),
}
}
}
impl<T> PartialEq for Outcome<T> {
fn eq(&self, other: &Outcome<T>) -> bool {
self.as_int() == other.as_int()
}
}
impl<T> fmt::Debug for Outcome<T> {
impl<S, E, F> fmt::Debug for Outcome<S, E, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Outcome::{}", self.as_str())
write!(f, "Outcome::{}", self.formatting().1)
}
}
impl<T> fmt::Display for Outcome<T> {
impl<S, E, F> fmt::Display for Outcome<S, E, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Outcome::Success => write!(f, "{}", Green.paint("Success")),
Outcome::Failure => write!(f, "{}", Red.paint("Failure")),
Outcome::Forward(..) => write!(f, "{}", Cyan.paint("Forwarding")),
}
let (color, string) = self.formatting();
write!(f, "{}", color.paint(string))
}
}

View File

@ -1,63 +1,61 @@
use std::fmt::Debug;
use outcome::Outcome;
use http::StatusCode;
use request::{Request, Data};
pub type DataOutcome<S, E> = Outcome<S, (StatusCode, E), Data>;
impl<S, E> DataOutcome<S, E> {
#[inline(always)]
pub fn of(result: Result<S, E>) -> Self {
match result {
Ok(val) => DataOutcome::success(val),
Err(err) => DataOutcome::failure(StatusCode::InternalServerError, err)
}
}
#[inline(always)]
pub fn success(s: S) -> Self {
Outcome::Success(s)
}
#[inline(always)]
pub fn failure(status: StatusCode, e: E) -> Self {
Outcome::Failure((status, e))
}
#[inline(always)]
pub fn forward(data: Data) -> Self {
Outcome::Forward(data)
}
}
/// Trait used to derive an object from incoming request data.
pub trait FromData: Sized {
type Error = ();
type Error;
fn from_data(request: &Request, data: Data) -> DataOutcome<Self, Self::Error>;
}
impl<T: FromData> FromData for Result<T, T::Error> {
type Error = ();
fn from_data(request: &Request, data: Data) -> DataOutcome<Self, Self::Error> {
match T::from_data(request, data) {
DataOutcome::Success(val) => DataOutcome::Success(Ok(val)),
DataOutcome::Failure(val) => DataOutcome::Success(Err(val)),
DataOutcome::Forward(data) => DataOutcome::Forward(data)
Outcome::Success(val) => DataOutcome::success(Ok(val)),
Outcome::Failure((_, val)) => DataOutcome::success(Err(val)),
Outcome::Forward(data) => DataOutcome::forward(data),
}
}
}
impl<T: FromData> FromData for Option<T> {
type Error = ();
fn from_data(request: &Request, data: Data) -> DataOutcome<Self, Self::Error> {
match T::from_data(request, data) {
DataOutcome::Success(val) => DataOutcome::Success(Some(val)),
DataOutcome::Failure(_) => DataOutcome::Success(None),
DataOutcome::Forward(data) => DataOutcome::Forward(data)
}
}
}
#[must_use]
pub enum DataOutcome<T, E> {
/// Signifies that all processing completed successfully.
Success(T),
/// Signifies that some processing occurred that ultimately resulted in
/// failure. As a result, no further processing can occur.
Failure(E),
/// Signifies that no processing occured and as such, processing can be
/// forwarded to the next available target.
Forward(Data),
}
impl<T, E: Debug> From<Result<T, E>> for DataOutcome<T, E> {
fn from(result: Result<T, E>) -> Self {
match result {
Ok(val) => DataOutcome::Success(val),
Err(e) => {
error_!("{:?}", e);
DataOutcome::Failure(e)
}
}
}
}
impl<T> From<Option<T>> for DataOutcome<T, ()> {
fn from(result: Option<T>) -> Self {
match result {
Some(val) => DataOutcome::Success(val),
None => DataOutcome::Failure(())
Outcome::Success(val) => DataOutcome::success(Some(val)),
Outcome::Failure(_) => DataOutcome::success(None),
Outcome::Forward(_) => DataOutcome::success(None)
}
}
}

View File

@ -155,7 +155,9 @@ impl Drop for Data {
}
impl FromData for Data {
type Error = ();
fn from_data(_: &Request, data: Data) -> DataOutcome<Self, Self::Error> {
DataOutcome::Success(data)
DataOutcome::success(data)
}
}

View File

@ -9,7 +9,7 @@
//! ```
//!
//! Form parameter types must implement the [FromForm](trait.FromForm.html)
//! trait, which is automatically derivable. Automatically deriving `FromForm`
//! trait, which is automaForwarp derivable. Automatically deriving `FromForm`
//! for a structure requires that all of its fields implement
//! [FromFormValue](trait.FormFormValue.html). See the
//! [codegen](/rocket_codegen/) documentation or the [forms guide](/guide/forms)
@ -27,6 +27,7 @@ use std::marker::PhantomData;
use std::fmt::{self, Debug};
use std::io::Read;
use http::StatusCode;
use request::{Request, FromData, Data, DataOutcome};
// This works, and it's safe, but it sucks to have the lifetime appear twice.
@ -103,20 +104,20 @@ impl<'f, T: FromForm<'f>> FromData for Form<'f, T> where T::Error: Debug {
fn from_data(request: &Request, data: Data) -> DataOutcome<Self, Self::Error> {
if !request.content_type().is_form() {
warn_!("Form data does not have form content type.");
return DataOutcome::Forward(data);
return DataOutcome::forward(data);
}
let mut form_string = String::with_capacity(4096);
let mut stream = data.open().take(32768);
if let Err(e) = stream.read_to_string(&mut form_string) {
error_!("IO Error: {:?}", e);
DataOutcome::Failure(None)
DataOutcome::failure(StatusCode::InternalServerError, None)
} else {
match Form::new(form_string) {
Ok(form) => DataOutcome::Success(form),
Ok(form) => DataOutcome::success(form),
Err((form_string, e)) => {
error_!("Failed to parse value from form: {:?}", e);
DataOutcome::Failure(Some(form_string))
DataOutcome::failure(StatusCode::BadRequest, Some(form_string))
}
}
}

View File

@ -1,62 +1,95 @@
use std::fmt::Debug;
use request::Request;
use http::{ContentType, Method, Cookies};
use outcome::Outcome;
use http::{StatusCode, ContentType, Method, Cookies};
pub type RequestOutcome<T, E> = Outcome<T, (StatusCode, E), ()>;
impl<T, E> RequestOutcome<T, E> {
#[inline(always)]
pub fn of(result: Result<T, E>) -> Self {
match result {
Ok(val) => Outcome::Success(val),
Err(_) => Outcome::Forward(())
}
}
#[inline(always)]
pub fn success(t: T) -> Self {
Outcome::Success(t)
}
#[inline(always)]
pub fn failure(code: StatusCode, error: E) -> Self {
Outcome::Failure((code, error))
}
#[inline(always)]
pub fn forward() -> Self {
Outcome::Forward(())
}
}
pub trait FromRequest<'r>: Sized {
type Error: Debug;
fn from_request(request: &'r Request) -> Result<Self, Self::Error>;
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error>;
}
impl<'r> FromRequest<'r> for &'r Request {
type Error = ();
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
Ok(request)
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> {
RequestOutcome::success(request)
}
}
impl<'r> FromRequest<'r> for Method {
type Error = ();
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
Ok(request.method)
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> {
RequestOutcome::success(request.method)
}
}
impl<'r> FromRequest<'r> for &'r Cookies {
type Error = ();
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
Ok(request.cookies())
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> {
RequestOutcome::success(request.cookies())
}
}
impl<'r> FromRequest<'r> for ContentType {
type Error = ();
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
Ok(request.content_type())
}
}
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T> {
type Error = ();
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
let opt = match T::from_request(request) {
Ok(v) => Some(v),
Err(_) => None,
};
Ok(opt)
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> {
RequestOutcome::success(request.content_type())
}
}
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result<T, T::Error> {
type Error = ();
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
Ok(T::from_request(request))
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> {
match T::from_request(request) {
Outcome::Success(val) => RequestOutcome::success(Ok(val)),
Outcome::Failure((_, e)) => RequestOutcome::success(Err(e)),
Outcome::Forward(_) => RequestOutcome::forward(),
}
}
}
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T> {
type Error = ();
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> {
match T::from_request(request) {
Outcome::Success(val) => RequestOutcome::success(Some(val)),
Outcome::Failure(_) => RequestOutcome::success(None),
Outcome::Forward(_) => RequestOutcome::success(None),
}
}
}

View File

@ -7,7 +7,7 @@ mod data;
mod from_request;
pub use self::request::Request;
pub use self::from_request::FromRequest;
pub use self::from_request::{FromRequest, RequestOutcome};
pub use self::param::{FromParam, FromSegments};
pub use self::form::{Form, FromForm, FromFormValue, FormItems};
pub use self::data::{Data, FromData, DataOutcome};

View File

@ -1,4 +1,4 @@
use response::{ResponseOutcome, Outcome, Responder};
use response::{ResponseOutcome, Responder};
use http::hyper::{FreshHyperResponse, StatusCode};
#[derive(Debug)]
@ -6,6 +6,6 @@ pub struct Failure(pub StatusCode);
impl Responder for Failure {
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
Outcome::Forward((self.0, res))
ResponseOutcome::forward(self.0, res)
}
}

View File

@ -1,7 +1,7 @@
use std::convert::AsRef;
use response::{ResponseOutcome, Responder};
use request::{Request, FromRequest};
use request::{Request, FromRequest, RequestOutcome};
use http::hyper::{HyperSetCookie, HyperCookiePair, FreshHyperResponse};
const FLASH_COOKIE_NAME: &'static str = "_flash";
@ -70,18 +70,12 @@ impl Flash<()> {
// TODO: Using Flash<()> is ugly. Either create a type FlashMessage = Flash<()>
// or create a Flash under request that does this.
// TODO: Consider not removing the 'flash' cookie until after this thing is
// dropped. This is because, at the moment, if Flash is including as a
// from_request param, and some other param fails, then the flash message will
// be dropped needlessly. This may or may not be the intended behavior.
// Alternatively, provide a guarantee about the order that from_request params
// will be evaluated and recommend that Flash is last.
impl<'r> FromRequest<'r> for Flash<()> {
type Error = ();
fn from_request(request: &'r Request) -> Result<Self, Self::Error> {
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> {
trace_!("Flash: attemping to retrieve message.");
request.cookies().find(FLASH_COOKIE_NAME).ok_or(()).and_then(|cookie| {
let r = request.cookies().find(FLASH_COOKIE_NAME).ok_or(()).and_then(|cookie| {
// Clear the flash message.
trace_!("Flash: retrieving message: {:?}", cookie);
request.cookies().remove(FLASH_COOKIE_NAME);
@ -96,6 +90,8 @@ impl<'r> FromRequest<'r> for Flash<()> {
let name_len: usize = len_str.parse().map_err(|_| ())?;
let (name, msg) = (&rest[..name_len], &rest[name_len..]);
Ok(Flash::named(name, msg))
})
});
RequestOutcome::of(r)
}
}

View File

@ -18,56 +18,63 @@ pub use self::data::Content;
pub use self::failure::Failure;
pub use outcome::Outcome;
use std::fmt;
use request::Data;
use http::hyper::{StatusCode, FreshHyperResponse};
use term_painter::Color::*;
use term_painter::ToStyle;
pub type ResponseOutcome<'a> = Outcome<(StatusCode, FreshHyperResponse<'a>)>;
pub type ResponseOutcome<'a> = Outcome<(), (), (StatusCode, FreshHyperResponse<'a>)>;
pub enum Response<'a> {
Forward(Data),
Complete(Box<Responder + 'a>)
impl<'a> ResponseOutcome<'a> {
#[inline(always)]
pub fn of<A, B>(result: Result<A, B>) -> Self {
match result {
Ok(_) => Outcome::Success(()),
Err(_) => Outcome::Failure(())
}
}
#[inline(always)]
pub fn success() -> ResponseOutcome<'a> {
Outcome::Success(())
}
#[inline(always)]
pub fn failure() -> ResponseOutcome<'a> {
Outcome::Failure(())
}
#[inline(always)]
pub fn forward(s: StatusCode, r: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
Outcome::Forward((s, r))
}
}
pub type Response<'a> = Outcome<Box<Responder + 'a>, StatusCode, Data>;
impl<'a> Response<'a> {
#[inline(always)]
pub fn complete<T: Responder + 'a>(body: T) -> Response<'a> {
Response::Complete(Box::new(body))
pub fn success<T: Responder + 'a>(responder: T) -> Response<'a> {
Outcome::Success(Box::new(responder))
}
#[inline(always)]
pub fn failure(code: StatusCode) -> Response<'static> {
Outcome::Failure(code)
}
#[inline(always)]
pub fn forward(data: Data) -> Response<'static> {
Response::Forward(data)
}
#[inline(always)]
pub fn failed(code: StatusCode) -> Response<'static> {
Response::complete(Failure(code))
Outcome::Forward(data)
}
#[inline(always)]
pub fn with_raw_status<T: Responder + 'a>(status: u16, body: T) -> Response<'a> {
let status_code = StatusCode::from_u16(status);
Response::complete(StatusResponse::new(status_code, body))
Response::success(StatusResponse::new(status_code, body))
}
#[doc(hidden)]
#[inline(always)]
pub fn responder(self) -> Option<Box<Responder + 'a>> {
match self {
Response::Complete(responder) => Some(responder),
_ => None
}
}
}
impl<'a> fmt::Display for Response<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Response::Complete(..) => write!(f, "{}", Green.paint("Complete")),
Response::Forward(..) => write!(f, "{}", Yellow.paint("Forwarding")),
}
self.succeeded()
}
}

View File

@ -1,4 +1,4 @@
use response::{ResponseOutcome, Outcome, Responder};
use response::{ResponseOutcome, Responder};
use http::hyper::{header, FreshHyperResponse, StatusCode};
#[derive(Debug)]
@ -31,6 +31,6 @@ impl<'a> Responder for Redirect {
res.headers_mut().set(header::ContentLength(0));
res.headers_mut().set(header::Location(self.1.clone()));
*(res.status_mut()) = self.0;
Outcome::of(res.send(b""))
ResponseOutcome::of(res.send(b""))
}
}

View File

@ -2,7 +2,7 @@ use std::io::{Read, Write};
use std::fs::File;
use std::fmt;
use response::{Outcome, ResponseOutcome};
use response::ResponseOutcome;
use http::mime::{Mime, TopLevel, SubLevel};
use http::hyper::{header, FreshHyperResponse, StatusCode};
@ -21,7 +21,7 @@ impl<'a> Responder for &'a str {
res.headers_mut().set(header::ContentType(mime));
}
Outcome::of(res.send(self.as_bytes()))
ResponseOutcome::of(res.send(self.as_bytes()))
}
}
@ -32,7 +32,7 @@ impl Responder for String {
res.headers_mut().set(header::ContentType(mime));
}
Outcome::of(res.send(self.as_bytes()))
ResponseOutcome::of(res.send(self.as_bytes()))
}
}
@ -42,18 +42,18 @@ impl Responder for File {
Ok(md) => md.len(),
Err(e) => {
error_!("Failed to read file metadata: {:?}", e);
return Outcome::Forward((StatusCode::InternalServerError, res));
return ResponseOutcome::forward(StatusCode::InternalServerError, res);
}
};
let mut v = Vec::new();
if let Err(e) = self.read_to_end(&mut v) {
error_!("Failed to read file: {:?}", e);
return Outcome::Forward((StatusCode::InternalServerError, res));
return ResponseOutcome::forward(StatusCode::InternalServerError, res);
}
res.headers_mut().set(header::ContentLength(size));
Outcome::of(res.start().and_then(|mut stream| stream.write_all(&v)))
ResponseOutcome::of(res.start().and_then(|mut stream| stream.write_all(&v)))
}
}
@ -63,7 +63,7 @@ impl<T: Responder> Responder for Option<T> {
val.respond(res)
} else {
warn_!("Response was `None`.");
Outcome::Forward((StatusCode::NotFound, res))
ResponseOutcome::forward(StatusCode::NotFound, res)
}
}
}
@ -75,7 +75,7 @@ impl<T: Responder, E: fmt::Debug> Responder for Result<T, E> {
Ok(ref mut val) => val.respond(res),
Err(ref e) => {
error_!("{:?}", e);
Outcome::Forward((StatusCode::InternalServerError, res))
ResponseOutcome::forward(StatusCode::InternalServerError, res)
}
}
}

View File

@ -1,6 +1,6 @@
use std::io::{Read, Write, ErrorKind};
use response::{Responder, Outcome, ResponseOutcome};
use response::{Responder, ResponseOutcome};
use http::hyper::FreshHyperResponse;
// TODO: Support custom chunk sizes.
@ -31,7 +31,7 @@ impl<T: Read> Responder for Stream<T> {
Ok(s) => s,
Err(e) => {
error_!("Failed opening response stream: {:?}", e);
return Outcome::Failure;
return ResponseOutcome::failure();
}
};
@ -46,22 +46,22 @@ impl<T: Read> Responder for Stream<T> {
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
Err(ref e) => {
error_!("Error streaming response: {:?}", e);
return Outcome::Failure;
return ResponseOutcome::failure();
}
}
}
if let Err(e) = stream.write_all(&buffer[..read]) {
error_!("Stream write_all() failed: {:?}", e);
return Outcome::Failure;
return ResponseOutcome::failure();
}
}
if let Err(e) = stream.end() {
error_!("Stream end() failed: {:?}", e);
return Outcome::Failure;
return ResponseOutcome::failure();
}
Outcome::Success
ResponseOutcome::success()
}
}

View File

@ -9,7 +9,6 @@ use term_painter::ToStyle;
use config;
use logger;
use request::{Request, Data, FormItems};
use response::Response;
use router::{Router, Route};
use catcher::{self, Catcher};
use outcome::Outcome;
@ -86,8 +85,11 @@ impl Rocket {
// to be forwarded. If it does, continue the loop to try again.
info_!("{} {}", White.paint("Response:"), response);
let mut responder = match response {
Response::Complete(responder) => responder,
Response::Forward(unused_data) => {
Outcome::Success(responder) => responder,
Outcome::Failure(status_code) => {
return self.handle_error(status_code, &request, res);
}
Outcome::Forward(unused_data) => {
data = unused_data;
continue;
}
@ -104,9 +106,9 @@ impl Rocket {
info_!("{} {}", White.paint("Outcome:"), outcome);
// Check if the responder wants to forward to a catcher.
match outcome {
Outcome::Forward((c, r)) => return self.handle_error(c, &request, r),
Outcome::Success | Outcome::Failure => return,
match outcome.forwarded() {
Some((c, r)) => return self.handle_error(c, &request, r),
None => return
};
}
@ -149,7 +151,7 @@ impl Rocket {
});
if let Some(mut responder) = catcher.handle(Error::NoRoute, req).responder() {
if responder.respond(response) != Outcome::Success {
if !responder.respond(response).is_success() {
error_!("Catcher outcome was unsuccessul; aborting response.");
} else {
info_!("Responded with catcher.");
@ -160,7 +162,7 @@ impl Rocket {
let catcher = self.default_catchers.get(&code.to_u16())
.unwrap_or(self.default_catchers.get(&500).expect("500 default"));
let responder = catcher.handle(Error::Internal, req).responder();
responder.unwrap().respond(response).expect_success()
responder.unwrap().respond(response).unwrap()
}
}

View File

@ -60,7 +60,7 @@ mod tests {
type SimpleRoute = (Method, &'static str);
fn dummy_handler(_req: &Request, _: Data) -> Response<'static> {
Response::complete("hi")
Response::success("hi")
}
fn m_collide(a: SimpleRoute, b: SimpleRoute) -> bool {

View File

@ -75,7 +75,7 @@ mod test {
use {Response, Request, Data};
fn dummy_handler(_req: &Request, _: Data) -> Response<'static> {
Response::complete("hi")
Response::success("hi")
}
fn router_with_routes(routes: &[&'static str]) -> Router {