diff --git a/codegen/src/decorators/route.rs b/codegen/src/decorators/route.rs index 67844c15..1a856e8b 100644 --- a/codegen/src/decorators/route.rs +++ b/codegen/src/decorators/route.rs @@ -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>, $query_statement $data_statement let result = $user_fn_name($fn_arguments); - ::rocket::Response::complete(result) + ::rocket::Response::success(result) } ).unwrap()); diff --git a/contrib/src/json/mod.rs b/contrib/src/json/mod.rs index dbab8a77..34c052c1 100644 --- a/contrib/src/json/mod.rs +++ b/contrib/src/json/mod.rs @@ -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 FromData for JSON { fn from_data(request: &Request, data: Data) -> DataOutcome { 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 Responder for JSON { 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) } } } diff --git a/contrib/src/templates/mod.rs b/contrib/src/templates/mod.rs index 81f080a3..4f8d068a 100644 --- a/contrib/src/templates/mod.rs +++ b/contrib/src/templates/mod.rs @@ -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), } } } diff --git a/examples/from_request/src/main.rs b/examples/from_request/src/main.rs index fa80cbe2..5807a64d 100644 --- a/examples/from_request/src/main.rs +++ b/examples/from_request/src/main.rs @@ -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 { - Ok(HeaderCount(request.headers().len())) + fn from_request(request: &'r Request) -> RequestOutcome { + RequestOutcome::success(HeaderCount(request.headers().len())) } } diff --git a/examples/manual_routes/src/main.rs b/examples/manual_routes/src/main.rs index 497d93c0..811e164b 100644 --- a/examples/manual_routes/src/main.rs +++ b/examples/manual_routes/src/main.rs @@ -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) } } diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 846c6780..20ce1099 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -7,7 +7,6 @@ authors = ["Sergio Benitez "] term-painter = "^0.2" log = "^0.3" url = "^1" -# mime = "^0.2" toml = "^0.2" [dependencies.hyper] diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 83a2c46b..a8fe0dc0 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -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; diff --git a/lib/src/outcome.rs b/lib/src/outcome.rs index 6531904a..6fa8e1f1 100644 --- a/lib/src/outcome.rs +++ b/lib/src/outcome.rs @@ -1,71 +1,115 @@ use std::fmt; use term_painter::Color::*; +use term_painter::Color; use term_painter::ToStyle; #[must_use] -pub enum Outcome { - /// 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 { + /// Contains the success value. + Success(S), + /// Contains the failure error value. + Failure(E), + /// Contains the value to forward on. + Forward(F), } -impl Outcome { - pub fn of(result: Result) -> Outcome { - if let Err(e) = result { - error_!("{:?}", e); - return Outcome::Failure; +impl Outcome { + /// 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` to `Option`. + /// + /// 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 { + match self { + Outcome::Success(val) => Some(val), + _ => None + } + } + + /// Converts from `Outcome` to `Option`. + /// + /// 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 { + match self { + Outcome::Failure(val) => Some(val), + _ => None + } + } + + /// Converts from `Outcome` to `Option`. + /// + /// 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 { + 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 PartialEq for Outcome { - fn eq(&self, other: &Outcome) -> bool { - self.as_int() == other.as_int() - } -} - -impl fmt::Debug for Outcome { +impl fmt::Debug for Outcome { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Outcome::{}", self.as_str()) + write!(f, "Outcome::{}", self.formatting().1) } } -impl fmt::Display for Outcome { +impl fmt::Display for Outcome { 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)) } } diff --git a/lib/src/request/data/from_data.rs b/lib/src/request/data/from_data.rs index 5d643a4b..edce083a 100644 --- a/lib/src/request/data/from_data.rs +++ b/lib/src/request/data/from_data.rs @@ -1,63 +1,61 @@ -use std::fmt::Debug; - +use outcome::Outcome; +use http::StatusCode; use request::{Request, Data}; +pub type DataOutcome = Outcome; + +impl DataOutcome { + #[inline(always)] + pub fn of(result: Result) -> 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; } impl FromData for Result { + type Error = (); + fn from_data(request: &Request, data: Data) -> DataOutcome { 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 FromData for Option { + type Error = (); + fn from_data(request: &Request, data: Data) -> DataOutcome { 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 { - /// 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 From> for DataOutcome { - fn from(result: Result) -> Self { - match result { - Ok(val) => DataOutcome::Success(val), - Err(e) => { - error_!("{:?}", e); - DataOutcome::Failure(e) - } - } - } -} - -impl From> for DataOutcome { - fn from(result: Option) -> 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) } } } diff --git a/lib/src/request/data/mod.rs b/lib/src/request/data/mod.rs index 1f0731d0..62976a77 100644 --- a/lib/src/request/data/mod.rs +++ b/lib/src/request/data/mod.rs @@ -155,7 +155,9 @@ impl Drop for Data { } impl FromData for Data { + type Error = (); + fn from_data(_: &Request, data: Data) -> DataOutcome { - DataOutcome::Success(data) + DataOutcome::success(data) } } diff --git a/lib/src/request/form/mod.rs b/lib/src/request/form/mod.rs index 9eec3ed2..9cacd6fc 100644 --- a/lib/src/request/form/mod.rs +++ b/lib/src/request/form/mod.rs @@ -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 { 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)) } } } diff --git a/lib/src/request/from_request.rs b/lib/src/request/from_request.rs index 4c94a891..6cd3b2cf 100644 --- a/lib/src/request/from_request.rs +++ b/lib/src/request/from_request.rs @@ -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 = Outcome; + +impl RequestOutcome { + #[inline(always)] + pub fn of(result: Result) -> 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; + fn from_request(request: &'r Request) -> RequestOutcome; } impl<'r> FromRequest<'r> for &'r Request { type Error = (); - fn from_request(request: &'r Request) -> Result { - Ok(request) + fn from_request(request: &'r Request) -> RequestOutcome { + RequestOutcome::success(request) } } impl<'r> FromRequest<'r> for Method { type Error = (); - fn from_request(request: &'r Request) -> Result { - Ok(request.method) + fn from_request(request: &'r Request) -> RequestOutcome { + RequestOutcome::success(request.method) } } impl<'r> FromRequest<'r> for &'r Cookies { type Error = (); - fn from_request(request: &'r Request) -> Result { - Ok(request.cookies()) + + fn from_request(request: &'r Request) -> RequestOutcome { + RequestOutcome::success(request.cookies()) } } impl<'r> FromRequest<'r> for ContentType { type Error = (); - fn from_request(request: &'r Request) -> Result { - Ok(request.content_type()) - } -} - -impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option { - type Error = (); - - fn from_request(request: &'r Request) -> Result { - let opt = match T::from_request(request) { - Ok(v) => Some(v), - Err(_) => None, - }; - - Ok(opt) + fn from_request(request: &'r Request) -> RequestOutcome { + RequestOutcome::success(request.content_type()) } } impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result { type Error = (); - fn from_request(request: &'r Request) -> Result { - Ok(T::from_request(request)) + fn from_request(request: &'r Request) -> RequestOutcome { + 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 { + type Error = (); + + fn from_request(request: &'r Request) -> RequestOutcome { + match T::from_request(request) { + Outcome::Success(val) => RequestOutcome::success(Some(val)), + Outcome::Failure(_) => RequestOutcome::success(None), + Outcome::Forward(_) => RequestOutcome::success(None), + } + } +} + diff --git a/lib/src/request/mod.rs b/lib/src/request/mod.rs index 8020add1..d69c565e 100644 --- a/lib/src/request/mod.rs +++ b/lib/src/request/mod.rs @@ -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}; diff --git a/lib/src/response/failure.rs b/lib/src/response/failure.rs index 9845936b..44393dea 100644 --- a/lib/src/response/failure.rs +++ b/lib/src/response/failure.rs @@ -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) } } diff --git a/lib/src/response/flash.rs b/lib/src/response/flash.rs index 637823aa..ae053000 100644 --- a/lib/src/response/flash.rs +++ b/lib/src/response/flash.rs @@ -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 { + fn from_request(request: &'r Request) -> RequestOutcome { 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) } } diff --git a/lib/src/response/mod.rs b/lib/src/response/mod.rs index aaf724fa..043b6efa 100644 --- a/lib/src/response/mod.rs +++ b/lib/src/response/mod.rs @@ -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) +impl<'a> ResponseOutcome<'a> { + #[inline(always)] + pub fn of(result: Result) -> 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, StatusCode, Data>; + impl<'a> Response<'a> { #[inline(always)] - pub fn complete(body: T) -> Response<'a> { - Response::Complete(Box::new(body)) + pub fn success(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(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> { - 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() } } diff --git a/lib/src/response/redirect.rs b/lib/src/response/redirect.rs index 7e55c2ef..bde41344 100644 --- a/lib/src/response/redirect.rs +++ b/lib/src/response/redirect.rs @@ -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"")) } } diff --git a/lib/src/response/responder.rs b/lib/src/response/responder.rs index 572a2b6b..b00aa0fa 100644 --- a/lib/src/response/responder.rs +++ b/lib/src/response/responder.rs @@ -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 Responder for Option { val.respond(res) } else { warn_!("Response was `None`."); - Outcome::Forward((StatusCode::NotFound, res)) + ResponseOutcome::forward(StatusCode::NotFound, res) } } } @@ -75,7 +75,7 @@ impl Responder for Result { Ok(ref mut val) => val.respond(res), Err(ref e) => { error_!("{:?}", e); - Outcome::Forward((StatusCode::InternalServerError, res)) + ResponseOutcome::forward(StatusCode::InternalServerError, res) } } } diff --git a/lib/src/response/result.rs b/lib/src/response/result.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/src/response/stream.rs b/lib/src/response/stream.rs index df6285f9..9916b3a3 100644 --- a/lib/src/response/stream.rs +++ b/lib/src/response/stream.rs @@ -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 Responder for Stream { Ok(s) => s, Err(e) => { error_!("Failed opening response stream: {:?}", e); - return Outcome::Failure; + return ResponseOutcome::failure(); } }; @@ -46,22 +46,22 @@ impl Responder for Stream { 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() } } diff --git a/lib/src/rocket.rs b/lib/src/rocket.rs index ffa1e3c2..9667a17b 100644 --- a/lib/src/rocket.rs +++ b/lib/src/rocket.rs @@ -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() } } diff --git a/lib/src/router/collider.rs b/lib/src/router/collider.rs index 7c758d44..03f327a9 100644 --- a/lib/src/router/collider.rs +++ b/lib/src/router/collider.rs @@ -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 { diff --git a/lib/src/router/mod.rs b/lib/src/router/mod.rs index 1b61b0f1..6352b20b 100644 --- a/lib/src/router/mod.rs +++ b/lib/src/router/mod.rs @@ -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 {