Make Outcome generic on its encapsulated type.

This commit is contained in:
Sergio Benitez 2016-10-07 19:09:05 -07:00
parent d321e1de8d
commit be3530bb44
14 changed files with 65 additions and 51 deletions

View File

@ -4,7 +4,7 @@ extern crate serde_json;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use rocket::request::{Request, FromRequest}; use rocket::request::{Request, FromRequest};
use rocket::response::{Responder, Outcome, data}; use rocket::response::{Responder, Outcome, ResponseOutcome, data};
use rocket::http::hyper::FreshHyperResponse; use rocket::http::hyper::FreshHyperResponse;
use self::serde::{Serialize, Deserialize}; use self::serde::{Serialize, Deserialize};
@ -70,7 +70,7 @@ impl<'r, 'c, T: Deserialize> FromRequest<'r, 'c> 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>) -> Outcome<'a> { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'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) => {

View File

@ -17,7 +17,7 @@ use std::path::{Path, PathBuf};
use std::collections::HashMap; use std::collections::HashMap;
use rocket::Rocket; use rocket::Rocket;
use rocket::response::{Content, Outcome, Responder}; use rocket::response::{Content, Outcome, ResponseOutcome, Responder};
use rocket::http::hyper::FreshHyperResponse; use rocket::http::hyper::FreshHyperResponse;
use rocket::http::ContentType; use rocket::http::ContentType;
@ -133,7 +133,7 @@ impl Template {
} }
impl Responder for Template { impl Responder for Template {
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'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()

9
lib/src/request/data.rs Normal file
View File

@ -0,0 +1,9 @@
use std::io::Read;
pub struct Data {
stream: Box<Read>
}
pub trait FromData {
fn from_data(data: Data) -> Outcome { }
}

View File

@ -1,4 +1,4 @@
use response::{Responder, Outcome}; use response::{Responder, ResponseOutcome};
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>) -> Outcome<'b> { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> ResponseOutcome<'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>) -> Outcome<'b> { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> ResponseOutcome<'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,6 +1,6 @@
use std::io::Write; use std::io::Write;
use response::{Outcome, Responder}; use response::{ResponseOutcome, Outcome, Responder};
use http::hyper::{header, FreshHyperResponse}; use http::hyper::{header, FreshHyperResponse};
use http::hyper::StatusCode; use http::hyper::StatusCode;
@ -13,20 +13,20 @@ impl Empty {
} }
impl Responder for Empty { impl Responder for Empty {
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a> { fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
res.headers_mut().set(header::ContentLength(0)); res.headers_mut().set(header::ContentLength(0));
*(res.status_mut()) = self.0; *(res.status_mut()) = self.0;
let mut stream = res.start().unwrap(); let mut stream = res.start().unwrap();
stream.write_all(b"").unwrap(); stream.write_all(b"").unwrap();
Outcome::Complete Outcome::Success
} }
} }
pub struct Forward; pub struct Forward;
impl Responder for Forward { impl Responder for Forward {
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
Outcome::FailForward(res) Outcome::FailForward(res)
} }
} }

View File

@ -1,6 +1,6 @@
use std::convert::AsRef; use std::convert::AsRef;
use response::{Outcome, Responder}; use response::{ResponseOutcome, Responder};
use request::{Request, FromRequest}; use request::{Request, FromRequest};
use http::hyper::{HyperSetCookie, HyperCookiePair, FreshHyperResponse}; use http::hyper::{HyperSetCookie, HyperCookiePair, FreshHyperResponse};
@ -43,7 +43,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>) -> Outcome<'b> { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> ResponseOutcome<'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)

View File

@ -13,7 +13,7 @@ pub use self::responder::Responder;
pub use self::empty::{Empty, Forward}; pub use self::empty::{Empty, Forward};
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::outcome::Outcome; pub use self::outcome::{Outcome, ResponseOutcome};
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;

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, Outcome}; use response::{Responder, ResponseOutcome};
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>) -> Outcome<'a> { fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> ResponseOutcome<'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

@ -5,21 +5,26 @@ use term_painter::ToStyle;
use http::hyper::FreshHyperResponse; use http::hyper::FreshHyperResponse;
pub enum Outcome<'h> { pub enum Outcome<T> {
/// Signifies a response that completed sucessfully. /// Signifies a response that completed sucessfully.
Complete, Success,
/// Signifies a response that failed internally. /// Signifies a failing response that started responding but fail, so no
Bad(FreshHyperResponse<'h>), /// further processing can occur.
/// Signifies a failing response where no further processing should happen.
FailStop, FailStop,
/// Signifies a failing response whose request should be processed further. /// Signifies a response that failed internally without beginning to
FailForward(FreshHyperResponse<'h>), /// respond but no further processing should occur.
Bad(T),
/// Signifies a failing response that failed internally without beginning to
/// respond. Further processing should be attempted.
FailForward(T),
} }
impl<'h> Outcome<'h> { pub type ResponseOutcome<'a> = Outcome<FreshHyperResponse<'a>>;
impl<T> Outcome<T> {
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
match *self { match *self {
Outcome::Complete => "Complete", Outcome::Success => "Success",
Outcome::FailStop => "FailStop", Outcome::FailStop => "FailStop",
Outcome::Bad(..) => "Bad", Outcome::Bad(..) => "Bad",
Outcome::FailForward(..) => "FailForward", Outcome::FailForward(..) => "FailForward",
@ -28,7 +33,7 @@ impl<'h> Outcome<'h> {
fn as_int(&self) -> isize { fn as_int(&self) -> isize {
match *self { match *self {
Outcome::Complete => 0, Outcome::Success => 0,
Outcome::Bad(..) => 1, Outcome::Bad(..) => 1,
Outcome::FailStop => 2, Outcome::FailStop => 2,
Outcome::FailForward(..) => 3, Outcome::FailForward(..) => 3,
@ -36,22 +41,22 @@ impl<'h> Outcome<'h> {
} }
} }
impl<'h> PartialEq for Outcome<'h> { impl<T> PartialEq for Outcome<T> {
fn eq(&self, other: &Outcome<'h>) -> bool { fn eq(&self, other: &Outcome<T>) -> bool {
self.as_int() == other.as_int() self.as_int() == other.as_int()
} }
} }
impl<'h> fmt::Debug for Outcome<'h> { impl<T> fmt::Debug for Outcome<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Outcome::{}", self.as_str()) write!(f, "Outcome::{}", self.as_str())
} }
} }
impl<'h> fmt::Display for Outcome<'h> { impl<T> fmt::Display for Outcome<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
Outcome::Complete => write!(f, "{}", Green.paint("Complete")), Outcome::Success => write!(f, "{}", Green.paint("Success")),
Outcome::Bad(..) => write!(f, "{}", Yellow.paint("Bad Completion")), Outcome::Bad(..) => write!(f, "{}", Yellow.paint("Bad Completion")),
Outcome::FailStop => write!(f, "{}", Red.paint("Failed")), Outcome::FailStop => write!(f, "{}", Red.paint("Failed")),
Outcome::FailForward(..) => write!(f, "{}", Cyan.paint("Forwarding")), Outcome::FailForward(..) => write!(f, "{}", Cyan.paint("Forwarding")),

View File

@ -1,4 +1,4 @@
use response::{Outcome, Responder}; use response::{ResponseOutcome, Outcome, Responder};
use http::hyper::{header, FreshHyperResponse, StatusCode}; use http::hyper::{header, FreshHyperResponse, StatusCode};
#[derive(Debug)] #[derive(Debug)]
@ -27,11 +27,11 @@ impl Redirect {
} }
impl<'a> Responder for Redirect { impl<'a> Responder for Redirect {
fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> Outcome<'b> { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> ResponseOutcome<'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;
res.send(b"").unwrap(); res.send(b"").unwrap();
Outcome::Complete Outcome::Success
} }
} }

View File

@ -2,7 +2,7 @@ use std::io::{Read, Write};
use std::fs::File; use std::fs::File;
use std::fmt; use std::fmt;
use response::Outcome; use response::{Outcome, 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};
@ -11,29 +11,29 @@ use http::hyper::{header, FreshHyperResponse, StatusCode};
// In particular, we want to try the next ranked route when when parsing // In particular, we want to try the next ranked route when when parsing
// parameters doesn't work out. // parameters doesn't work out.
pub trait Responder { pub trait Responder {
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a>; fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> ResponseOutcome<'a>;
} }
impl<'a> Responder for &'a str { impl<'a> Responder for &'a str {
fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> Outcome<'b> { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> ResponseOutcome<'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));
} }
res.send(self.as_bytes()).unwrap(); res.send(self.as_bytes()).unwrap();
Outcome::Complete Outcome::Success
} }
} }
impl Responder for String { impl Responder for String {
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a> { fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> ResponseOutcome<'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));
} }
res.send(self.as_bytes()).unwrap(); res.send(self.as_bytes()).unwrap();
Outcome::Complete Outcome::Success
} }
} }
@ -42,7 +42,7 @@ impl Responder for String {
// a way to retrieve a file based on its fd, strangely enough. See... // a way to retrieve a file based on its fd, strangely enough. See...
// https://stackoverflow.com/questions/1188757/getting-filename-from-file-descriptor-in-c // https://stackoverflow.com/questions/1188757/getting-filename-from-file-descriptor-in-c
impl Responder for File { impl Responder for File {
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a> { fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
let size = self.metadata().unwrap().len(); let size = self.metadata().unwrap().len();
res.headers_mut().set(header::ContentLength(size)); res.headers_mut().set(header::ContentLength(size));
@ -53,12 +53,12 @@ impl Responder for File {
let mut stream = res.start().unwrap(); let mut stream = res.start().unwrap();
stream.write_all(&v).unwrap(); stream.write_all(&v).unwrap();
Outcome::Complete Outcome::Success
} }
} }
impl<T: Responder> Responder for Option<T> { impl<T: Responder> Responder for Option<T> {
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
if self.is_none() { if self.is_none() {
trace!("Option is none."); trace!("Option is none.");
// TODO: Should this be a 404 or 500? // TODO: Should this be a 404 or 500?
@ -71,7 +71,7 @@ impl<T: Responder> Responder for Option<T> {
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>) -> Outcome<'a> { default fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
if self.is_err() { if self.is_err() {
error_!("{:?}", self.as_ref().err().unwrap()); error_!("{:?}", self.as_ref().err().unwrap());
// TODO: Should this be a 404 or 500? // TODO: Should this be a 404 or 500?
@ -83,7 +83,7 @@ impl<T: Responder, E: fmt::Debug> Responder for Result<T, E> {
} }
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>) -> Outcome<'a> { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'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,6 +1,6 @@
use std::io::{Read, Write, ErrorKind}; use std::io::{Read, Write, ErrorKind};
use response::{Responder, Outcome}; use response::{Responder, Outcome, ResponseOutcome};
use http::hyper::FreshHyperResponse; use http::hyper::FreshHyperResponse;
// TODO: Support custom chunk sizes. // TODO: Support custom chunk sizes.
@ -26,7 +26,7 @@ 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>) -> Outcome<'a> { fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> ResponseOutcome<'a> {
let mut stream = res.start().unwrap(); let mut stream = res.start().unwrap();
let mut buffer = [0; CHUNK_SIZE]; let mut buffer = [0; CHUNK_SIZE];
let mut complete = false; let mut complete = false;
@ -55,6 +55,6 @@ impl<T: Read> Responder for Stream<T> {
return Outcome::FailStop; return Outcome::FailStop;
} }
Outcome::Complete Outcome::Success
} }
} }

View File

@ -1,4 +1,4 @@
use response::{Responder, Outcome}; use response::{Responder, ResponseOutcome};
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>) -> Outcome<'b> { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> ResponseOutcome<'b> {
*(res.status_mut()) = self.status; *(res.status_mut()) = self.status;
self.responder.respond(res) self.responder.respond(res)
} }

View File

@ -75,7 +75,7 @@ impl Rocket {
// Get the result if we failed forward so we can try again. // Get the result if we failed forward so we can try again.
res = match outcome { res = match outcome {
Outcome::Complete | Outcome::FailStop => return, Outcome::Success | Outcome::FailStop => return,
Outcome::FailForward(r) => r, Outcome::FailForward(r) => r,
Outcome::Bad(r) => return self.handle_internal_error(&request, r), Outcome::Bad(r) => return self.handle_internal_error(&request, r),
}; };