Remove RequestOutcome, ResponseOutcome in favor of Outcome. Remove Failure response type.

This commit is contained in:
Sergio Benitez 2016-10-25 11:17:49 +02:00
parent 6a6efaf56b
commit 5447f81f77
16 changed files with 128 additions and 160 deletions

View File

@ -4,8 +4,9 @@ extern crate serde_json;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::io::Read; use std::io::Read;
use rocket::outcome::Outcome::*;
use rocket::request::{Request, Data, FromData, DataOutcome}; use rocket::request::{Request, Data, FromData, DataOutcome};
use rocket::response::{Responder, ResponseOutcome, data}; use rocket::response::{self, Responder, data};
use rocket::http::StatusCode; use rocket::http::StatusCode;
use rocket::http::hyper::FreshHyperResponse; use rocket::http::hyper::FreshHyperResponse;
@ -82,12 +83,12 @@ impl<T: Deserialize> FromData for JSON<T> {
} }
impl<T: Serialize> Responder for JSON<T> { impl<T: Serialize> Responder for JSON<T> {
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> response::Outcome<'a> {
match serde_json::to_string(&self.0) { match serde_json::to_string(&self.0) {
Ok(json_string) => data::JSON(json_string).respond(res), Ok(json_string) => data::JSON(json_string).respond(res),
Err(e) => { Err(e) => {
error_!("JSON failed to serialize: {:?}", e); error_!("JSON failed to serialize: {:?}", e);
ResponseOutcome::forward(StatusCode::BadRequest, res) Forward((StatusCode::BadRequest, res))
} }
} }
} }

View File

@ -17,9 +17,10 @@ use std::path::{Path, PathBuf};
use std::collections::HashMap; use std::collections::HashMap;
use rocket::config; use rocket::config;
use rocket::response::{Content, ResponseOutcome, Responder}; use rocket::response::{Content, Outcome, Responder};
use rocket::http::hyper::FreshHyperResponse; use rocket::http::hyper::FreshHyperResponse;
use rocket::http::{ContentType, StatusCode}; use rocket::http::{ContentType, StatusCode};
use rocket::Outcome::*;
/// The Template type implements generic support for template rendering in /// The Template type implements generic support for template rendering in
/// Rocket. /// Rocket.
@ -159,7 +160,7 @@ impl Template {
} }
impl Responder for Template { impl Responder for Template {
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> {
let content_type = match self.1 { let content_type = match self.1 {
Some(ref ext) => ContentType::from_extension(ext), Some(ref ext) => ContentType::from_extension(ext),
None => ContentType::html() None => ContentType::html()
@ -167,7 +168,7 @@ impl Responder for Template {
match self.0 { match self.0 {
Some(ref render) => Content(content_type, render.as_str()).respond(res), Some(ref render) => Content(content_type, render.as_str()).respond(res),
None => ResponseOutcome::forward(StatusCode::InternalServerError, res), None => Forward((StatusCode::InternalServerError, res)),
} }
} }
} }

View File

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

View File

@ -132,6 +132,7 @@ pub use request::{Request, Data};
pub use error::Error; pub use error::Error;
pub use catcher::Catcher; pub use catcher::Catcher;
pub use rocket::Rocket; pub use rocket::Rocket;
pub use outcome::{Outcome, IntoOutcome};
/// Alias to Rocket::ignite(). /// Alias to Rocket::ignite().
pub fn ignite() -> Rocket { pub fn ignite() -> Rocket {

View File

@ -85,6 +85,9 @@ use term_painter::Color::*;
use term_painter::Color; use term_painter::Color;
use term_painter::ToStyle; use term_painter::ToStyle;
use self::Outcome::*;
use http::hyper::{FreshHyperResponse, StatusCode};
/// An enum representing success (`Success`), failure (`Failure`), or /// An enum representing success (`Success`), failure (`Failure`), or
/// forwarding (`Forward`). /// forwarding (`Forward`).
#[must_use] #[must_use]
@ -98,6 +101,28 @@ pub enum Outcome<S, E, F> {
Forward(F), Forward(F),
} }
pub trait IntoOutcome<S, E, F> {
fn into_outcome(self) -> Outcome<S, E, F>;
}
impl<T, E> IntoOutcome<T, (StatusCode, E), ()> for Result<T, E> {
fn into_outcome(self) -> Outcome<T, (StatusCode, E), ()> {
match self {
Ok(val) => Success(val),
Err(val) => Failure((StatusCode::BadRequest, val))
}
}
}
impl<'a, T, E> IntoOutcome<(), (), (StatusCode, FreshHyperResponse<'a>)> for Result<T, E> {
fn into_outcome(self) -> Outcome<(), (), (StatusCode, FreshHyperResponse<'a>)> {
match self {
Ok(_) => Success(()),
Err(_) => Failure(())
}
}
}
impl<S, E, F> Outcome<S, E, F> { impl<S, E, F> Outcome<S, E, F> {
/// Unwraps the Outcome, yielding the contents of a Success. /// Unwraps the Outcome, yielding the contents of a Success.
/// ///
@ -117,7 +142,7 @@ impl<S, E, F> Outcome<S, E, F> {
#[inline(always)] #[inline(always)]
pub fn unwrap(self) -> S { pub fn unwrap(self) -> S {
match self { match self {
Outcome::Success(val) => val, Success(val) => val,
_ => panic!("Expected a successful outcome!") _ => panic!("Expected a successful outcome!")
} }
} }
@ -142,7 +167,7 @@ impl<S, E, F> Outcome<S, E, F> {
#[inline(always)] #[inline(always)]
pub fn is_success(&self) -> bool { pub fn is_success(&self) -> bool {
match *self { match *self {
Outcome::Success(_) => true, Success(_) => true,
_ => false _ => false
} }
} }
@ -167,7 +192,7 @@ impl<S, E, F> Outcome<S, E, F> {
#[inline(always)] #[inline(always)]
pub fn is_failure(&self) -> bool { pub fn is_failure(&self) -> bool {
match *self { match *self {
Outcome::Failure(_) => true, Failure(_) => true,
_ => false _ => false
} }
} }
@ -192,7 +217,7 @@ impl<S, E, F> Outcome<S, E, F> {
#[inline(always)] #[inline(always)]
pub fn is_forward(&self) -> bool { pub fn is_forward(&self) -> bool {
match *self { match *self {
Outcome::Forward(_) => true, Forward(_) => true,
_ => false _ => false
} }
} }
@ -218,7 +243,7 @@ impl<S, E, F> Outcome<S, E, F> {
#[inline(always)] #[inline(always)]
pub fn succeeded(self) -> Option<S> { pub fn succeeded(self) -> Option<S> {
match self { match self {
Outcome::Success(val) => Some(val), Success(val) => Some(val),
_ => None _ => None
} }
} }
@ -244,7 +269,7 @@ impl<S, E, F> Outcome<S, E, F> {
#[inline(always)] #[inline(always)]
pub fn failed(self) -> Option<E> { pub fn failed(self) -> Option<E> {
match self { match self {
Outcome::Failure(val) => Some(val), Failure(val) => Some(val),
_ => None _ => None
} }
} }
@ -270,7 +295,7 @@ impl<S, E, F> Outcome<S, E, F> {
#[inline(always)] #[inline(always)]
pub fn forwarded(self) -> Option<F> { pub fn forwarded(self) -> Option<F> {
match self { match self {
Outcome::Forward(val) => Some(val), Forward(val) => Some(val),
_ => None _ => None
} }
} }
@ -290,9 +315,9 @@ impl<S, E, F> Outcome<S, E, F> {
#[inline(always)] #[inline(always)]
pub fn as_ref(&self) -> Outcome<&S, &E, &F> { pub fn as_ref(&self) -> Outcome<&S, &E, &F> {
match *self { match *self {
Outcome::Success(ref val) => Outcome::Success(val), Success(ref val) => Success(val),
Outcome::Failure(ref val) => Outcome::Failure(val), Failure(ref val) => Failure(val),
Outcome::Forward(ref val) => Outcome::Forward(val), Forward(ref val) => Forward(val),
} }
} }
@ -312,18 +337,18 @@ impl<S, E, F> Outcome<S, E, F> {
#[inline(always)] #[inline(always)]
pub fn as_mut(&mut self) -> Outcome<&mut S, &mut E, &mut F> { pub fn as_mut(&mut self) -> Outcome<&mut S, &mut E, &mut F> {
match *self { match *self {
Outcome::Success(ref mut val) => Outcome::Success(val), Success(ref mut val) => Success(val),
Outcome::Failure(ref mut val) => Outcome::Failure(val), Failure(ref mut val) => Failure(val),
Outcome::Forward(ref mut val) => Outcome::Forward(val), Forward(ref mut val) => Forward(val),
} }
} }
#[inline(always)] #[inline(always)]
fn formatting(&self) -> (Color, &'static str) { fn formatting(&self) -> (Color, &'static str) {
match *self { match *self {
Outcome::Success(..) => (Green, "Succcess"), Success(..) => (Green, "Succcess"),
Outcome::Failure(..) => (Red, "Failure"), Failure(..) => (Red, "Failure"),
Outcome::Forward(..) => (Yellow, "Forward"), Forward(..) => (Yellow, "Forward"),
} }
} }
} }

View File

@ -1,83 +1,59 @@
use std::fmt::Debug; use std::fmt::Debug;
use outcome;
use request::Request; use request::Request;
use outcome::Outcome; use outcome::Outcome::*;
use http::{StatusCode, ContentType, Method, Cookies}; use http::{StatusCode, ContentType, Method, Cookies};
/// Type alias for the `Outcome` of a `FromRequest` conversion. /// Type alias for the `Outcome` of a `FromRequest` conversion.
pub type RequestOutcome<T, E> = Outcome<T, (StatusCode, E), ()>; pub type Outcome<T, E> = outcome::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 { pub trait FromRequest<'r>: Sized {
type Error: Debug; type Error: Debug;
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error>; fn from_request(request: &'r Request) -> Outcome<Self, Self::Error>;
} }
impl<'r> FromRequest<'r> for &'r Request { impl<'r> FromRequest<'r> for &'r Request {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> { fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> {
RequestOutcome::success(request) Success(request)
} }
} }
impl<'r> FromRequest<'r> for Method { impl<'r> FromRequest<'r> for Method {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> { fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> {
RequestOutcome::success(request.method) Success(request.method)
} }
} }
impl<'r> FromRequest<'r> for &'r Cookies { impl<'r> FromRequest<'r> for &'r Cookies {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> { fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> {
RequestOutcome::success(request.cookies()) Success(request.cookies())
} }
} }
impl<'r> FromRequest<'r> for ContentType { impl<'r> FromRequest<'r> for ContentType {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> { fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> {
RequestOutcome::success(request.content_type()) Success(request.content_type())
} }
} }
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result<T, T::Error> { impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result<T, T::Error> {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> { fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> {
match T::from_request(request) { match T::from_request(request) {
Outcome::Success(val) => RequestOutcome::success(Ok(val)), Success(val) => Success(Ok(val)),
Outcome::Failure((_, e)) => RequestOutcome::success(Err(e)), Failure((_, e)) => Success(Err(e)),
Outcome::Forward(_) => RequestOutcome::forward(), Forward(_) => Forward(()),
} }
} }
} }
@ -85,11 +61,11 @@ impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result<T, T::Error> {
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T> { impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T> {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> { fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> {
match T::from_request(request) { match T::from_request(request) {
Outcome::Success(val) => RequestOutcome::success(Some(val)), Success(val) => Success(Some(val)),
Outcome::Failure(_) => RequestOutcome::success(None), Failure(_) => Success(None),
Outcome::Forward(_) => RequestOutcome::success(None), Forward(_) => Success(None),
} }
} }
} }

View File

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

View File

@ -1,4 +1,4 @@
use response::{Responder, ResponseOutcome}; use response::{Responder, Outcome};
use http::hyper::{header, FreshHyperResponse}; use http::hyper::{header, FreshHyperResponse};
use http::mime::{Mime, TopLevel, SubLevel}; use http::mime::{Mime, TopLevel, SubLevel};
use http::ContentType; use http::ContentType;
@ -6,7 +6,7 @@ use http::ContentType;
pub struct Content<T: Responder>(pub ContentType, pub T); pub struct Content<T: Responder>(pub ContentType, pub T);
impl<T: Responder> Responder for Content<T> { impl<T: Responder> Responder for Content<T> {
fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> ResponseOutcome<'b> { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> Outcome<'b> {
res.headers_mut().set(header::ContentType(self.0.clone().into())); res.headers_mut().set(header::ContentType(self.0.clone().into()));
self.1.respond(res) self.1.respond(res)
} }
@ -17,7 +17,7 @@ macro_rules! impl_data_type_responder {
pub struct $name<T: Responder>(pub T); pub struct $name<T: Responder>(pub T);
impl<T: Responder> Responder for $name<T> { impl<T: Responder> Responder for $name<T> {
fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> ResponseOutcome<'b> { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> Outcome<'b> {
let mime = Mime(TopLevel::$top, SubLevel::$sub, vec![]); let mime = Mime(TopLevel::$top, SubLevel::$sub, vec![]);
res.headers_mut().set(header::ContentType(mime)); res.headers_mut().set(header::ContentType(mime));
self.0.respond(res) self.0.respond(res)

View File

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

View File

@ -1,7 +1,8 @@
use std::convert::AsRef; use std::convert::AsRef;
use response::{ResponseOutcome, Responder}; use outcome::IntoOutcome;
use request::{Request, FromRequest, RequestOutcome}; use response::{self, Responder};
use request::{self, Request, FromRequest};
use http::hyper::{HyperSetCookie, HyperCookiePair, FreshHyperResponse}; use http::hyper::{HyperSetCookie, HyperCookiePair, FreshHyperResponse};
const FLASH_COOKIE_NAME: &'static str = "_flash"; const FLASH_COOKIE_NAME: &'static str = "_flash";
@ -43,7 +44,7 @@ impl<R: Responder> Flash<R> {
} }
impl<R: Responder> Responder for Flash<R> { impl<R: Responder> Responder for Flash<R> {
fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> ResponseOutcome<'b> { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> response::Outcome<'b> {
trace_!("Flash: setting message: {}:{}", self.name, self.message); trace_!("Flash: setting message: {}:{}", self.name, self.message);
res.headers_mut().set(HyperSetCookie(vec![self.cookie_pair()])); res.headers_mut().set(HyperSetCookie(vec![self.cookie_pair()]));
self.responder.respond(res) self.responder.respond(res)
@ -73,7 +74,7 @@ impl Flash<()> {
impl<'r> FromRequest<'r> for Flash<()> { impl<'r> FromRequest<'r> for Flash<()> {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> RequestOutcome<Self, Self::Error> { fn from_request(request: &'r Request) -> request::Outcome<Self, Self::Error> {
trace_!("Flash: attemping to retrieve message."); trace_!("Flash: attemping to retrieve message.");
let r = 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. // Clear the flash message.
@ -92,6 +93,6 @@ impl<'r> FromRequest<'r> for Flash<()> {
Ok(Flash::named(name, msg)) Ok(Flash::named(name, msg))
}); });
RequestOutcome::of(r) r.into_outcome()
} }
} }

View File

@ -4,66 +4,38 @@ mod with_status;
mod flash; mod flash;
mod named_file; mod named_file;
mod stream; mod stream;
mod failure;
pub mod data; pub mod data;
pub use self::responder::Responder; pub use self::responder::{Outcome, Responder};
pub use self::redirect::Redirect; pub use self::redirect::Redirect;
pub use self::with_status::StatusResponse; pub use self::with_status::StatusResponse;
pub use self::flash::Flash; pub use self::flash::Flash;
pub use self::named_file::NamedFile; pub use self::named_file::NamedFile;
pub use self::stream::Stream; pub use self::stream::Stream;
pub use self::data::Content; pub use self::data::Content;
pub use self::failure::Failure;
pub use outcome::Outcome;
use outcome;
use request::Data; use request::Data;
use http::hyper::{StatusCode, FreshHyperResponse}; use http::hyper::StatusCode;
use outcome::Outcome::*;
pub type ResponseOutcome<'a> = Outcome<(), (), (StatusCode, FreshHyperResponse<'a>)>; pub type Response<'a> = outcome::Outcome<Box<Responder + 'a>, StatusCode, Data>;
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> { impl<'a> Response<'a> {
#[inline(always)] #[inline(always)]
pub fn success<T: Responder + 'a>(responder: T) -> Response<'a> { pub fn success<T: Responder + 'a>(responder: T) -> Response<'a> {
Outcome::Success(Box::new(responder)) Success(Box::new(responder))
} }
#[inline(always)] #[inline(always)]
pub fn failure(code: StatusCode) -> Response<'static> { pub fn failure(code: StatusCode) -> Response<'static> {
Outcome::Failure(code) Failure(code)
} }
#[inline(always)] #[inline(always)]
pub fn forward(data: Data) -> Response<'static> { pub fn forward(data: Data) -> Response<'static> {
Outcome::Forward(data) Forward(data)
} }
#[inline(always)] #[inline(always)]

View File

@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
use std::io; use std::io;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use response::{Responder, ResponseOutcome}; use response::{Responder, Outcome};
use http::hyper::{header, FreshHyperResponse}; use http::hyper::{header, FreshHyperResponse};
use http::ContentType; use http::ContentType;
@ -33,7 +33,7 @@ impl NamedFile {
} }
impl Responder for NamedFile { impl Responder for NamedFile {
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> { fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a> {
if let Some(ext) = self.path().extension() { if let Some(ext) = self.path().extension() {
let ext_string = ext.to_string_lossy().to_lowercase(); let ext_string = ext.to_string_lossy().to_lowercase();
let content_type = ContentType::from_extension(&ext_string); let content_type = ContentType::from_extension(&ext_string);

View File

@ -1,5 +1,6 @@
use response::{ResponseOutcome, Responder}; use response::{Outcome, Responder};
use http::hyper::{header, FreshHyperResponse, StatusCode}; use http::hyper::{header, FreshHyperResponse, StatusCode};
use outcome::IntoOutcome;
#[derive(Debug)] #[derive(Debug)]
pub struct Redirect(StatusCode, String); pub struct Redirect(StatusCode, String);
@ -27,10 +28,10 @@ impl Redirect {
} }
impl<'a> Responder for Redirect { impl<'a> Responder for Redirect {
fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> ResponseOutcome<'b> { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> Outcome<'b> {
res.headers_mut().set(header::ContentLength(0)); res.headers_mut().set(header::ContentLength(0));
res.headers_mut().set(header::Location(self.1.clone())); res.headers_mut().set(header::Location(self.1.clone()));
*(res.status_mut()) = self.0; *(res.status_mut()) = self.0;
ResponseOutcome::of(res.send(b"")) res.send(b"").into_outcome()
} }
} }

View File

@ -2,87 +2,86 @@ use std::io::{Read, Write};
use std::fs::File; use std::fs::File;
use std::fmt; use std::fmt;
use response::ResponseOutcome;
use http::mime::{Mime, TopLevel, SubLevel}; use http::mime::{Mime, TopLevel, SubLevel};
use http::hyper::{header, FreshHyperResponse, StatusCode}; use http::hyper::{header, FreshHyperResponse, StatusCode};
use outcome::{self, IntoOutcome};
use outcome::Outcome::*;
pub type Outcome<'a> = outcome::Outcome<(), (), (StatusCode, FreshHyperResponse<'a>)>;
// TODO: Have this return something saying whether every was okay. Need
// something like to be able to forward requests on when things don't work out.
// In particular, we want to try the next ranked route when when parsing
// parameters doesn't work out.
pub trait Responder { pub trait Responder {
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> ResponseOutcome<'a>; fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a>;
} }
impl<'a> Responder for &'a str { impl<'a> Responder for &'a str {
fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> ResponseOutcome<'b> { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> Outcome<'b> {
if res.headers().get::<header::ContentType>().is_none() { if res.headers().get::<header::ContentType>().is_none() {
let mime = Mime(TopLevel::Text, SubLevel::Plain, vec![]); let mime = Mime(TopLevel::Text, SubLevel::Plain, vec![]);
res.headers_mut().set(header::ContentType(mime)); res.headers_mut().set(header::ContentType(mime));
} }
ResponseOutcome::of(res.send(self.as_bytes())) res.send(self.as_bytes()).into_outcome()
} }
} }
impl Responder for String { impl Responder for String {
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> { fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a> {
if res.headers().get::<header::ContentType>().is_none() { if res.headers().get::<header::ContentType>().is_none() {
let mime = Mime(TopLevel::Text, SubLevel::Html, vec![]); let mime = Mime(TopLevel::Text, SubLevel::Html, vec![]);
res.headers_mut().set(header::ContentType(mime)); res.headers_mut().set(header::ContentType(mime));
} }
ResponseOutcome::of(res.send(self.as_bytes())) res.send(self.as_bytes()).into_outcome()
} }
} }
impl Responder for File { impl Responder for File {
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> { fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a> {
let size = match self.metadata() { let size = match self.metadata() {
Ok(md) => md.len(), Ok(md) => md.len(),
Err(e) => { Err(e) => {
error_!("Failed to read file metadata: {:?}", e); error_!("Failed to read file metadata: {:?}", e);
return ResponseOutcome::forward(StatusCode::InternalServerError, res); return Forward((StatusCode::InternalServerError, res));
} }
}; };
let mut v = Vec::new(); let mut v = Vec::new();
if let Err(e) = self.read_to_end(&mut v) { if let Err(e) = self.read_to_end(&mut v) {
error_!("Failed to read file: {:?}", e); error_!("Failed to read file: {:?}", e);
return ResponseOutcome::forward(StatusCode::InternalServerError, res); return Forward((StatusCode::InternalServerError, res));
} }
res.headers_mut().set(header::ContentLength(size)); res.headers_mut().set(header::ContentLength(size));
ResponseOutcome::of(res.start().and_then(|mut stream| stream.write_all(&v))) res.start().and_then(|mut stream| stream.write_all(&v)).into_outcome()
} }
} }
impl<T: Responder> Responder for Option<T> { impl<T: Responder> Responder for Option<T> {
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> {
if let Some(ref mut val) = *self { if let Some(ref mut val) = *self {
val.respond(res) val.respond(res)
} else { } else {
warn_!("Response was `None`."); warn_!("Response was `None`.");
ResponseOutcome::forward(StatusCode::NotFound, res) Forward((StatusCode::NotFound, res))
} }
} }
} }
impl<T: Responder, E: fmt::Debug> Responder for Result<T, E> { impl<T: Responder, E: fmt::Debug> Responder for Result<T, E> {
// prepend with `default` when using impl specialization // prepend with `default` when using impl specialization
default fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> { default fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> {
match *self { match *self {
Ok(ref mut val) => val.respond(res), Ok(ref mut val) => val.respond(res),
Err(ref e) => { Err(ref e) => {
error_!("{:?}", e); error_!("{:?}", e);
ResponseOutcome::forward(StatusCode::InternalServerError, res) Forward((StatusCode::InternalServerError, res))
} }
} }
} }
} }
impl<T: Responder, E: Responder + fmt::Debug> Responder for Result<T, E> { impl<T: Responder, E: Responder + fmt::Debug> Responder for Result<T, E> {
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> {
match *self { match *self {
Ok(ref mut responder) => responder.respond(res), Ok(ref mut responder) => responder.respond(res),
Err(ref mut responder) => responder.respond(res), Err(ref mut responder) => responder.respond(res),

View File

@ -1,7 +1,8 @@
use std::io::{Read, Write, ErrorKind}; use std::io::{Read, Write, ErrorKind};
use response::{Responder, ResponseOutcome}; use response::{Responder, Outcome};
use http::hyper::FreshHyperResponse; use http::hyper::FreshHyperResponse;
use outcome::Outcome::*;
// TODO: Support custom chunk sizes. // TODO: Support custom chunk sizes.
/// The default size of each chunk in the streamed response. /// The default size of each chunk in the streamed response.
@ -26,12 +27,12 @@ impl<T: Read> Stream<T> {
} }
impl<T: Read> Responder for Stream<T> { impl<T: Read> Responder for Stream<T> {
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> {
let mut stream = match res.start() { let mut stream = match res.start() {
Ok(s) => s, Ok(s) => s,
Err(e) => { Err(ref err) => {
error_!("Failed opening response stream: {:?}", e); error_!("Failed opening response stream: {:?}", err);
return ResponseOutcome::failure(); return Failure(());
} }
}; };
@ -46,22 +47,22 @@ impl<T: Read> Responder for Stream<T> {
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
Err(ref e) => { Err(ref e) => {
error_!("Error streaming response: {:?}", e); error_!("Error streaming response: {:?}", e);
return ResponseOutcome::failure(); return Failure(());
} }
} }
} }
if let Err(e) = stream.write_all(&buffer[..read]) { if let Err(e) = stream.write_all(&buffer[..read]) {
error_!("Stream write_all() failed: {:?}", e); error_!("Stream write_all() failed: {:?}", e);
return ResponseOutcome::failure(); return Failure(());
} }
} }
if let Err(e) = stream.end() { if let Err(e) = stream.end() {
error_!("Stream end() failed: {:?}", e); error_!("Stream end() failed: {:?}", e);
return ResponseOutcome::failure(); return Failure(());
} }
ResponseOutcome::success() Success(())
} }
} }

View File

@ -1,4 +1,4 @@
use response::{Responder, ResponseOutcome}; use response::{Responder, Outcome};
use http::hyper::{StatusCode, FreshHyperResponse}; use http::hyper::{StatusCode, FreshHyperResponse};
pub struct StatusResponse<R: Responder> { pub struct StatusResponse<R: Responder> {
@ -16,7 +16,7 @@ impl<R: Responder> StatusResponse<R> {
} }
impl<R: Responder> Responder for StatusResponse<R> { impl<R: Responder> Responder for StatusResponse<R> {
fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> ResponseOutcome<'b> { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> Outcome<'b> {
*(res.status_mut()) = self.status; *(res.status_mut()) = self.status;
self.responder.respond(res) self.responder.respond(res)
} }