mirror of https://github.com/rwf2/Rocket.git
Remove 'Result' specialization. Add 'Debug' responder.
This removes all uses of specialization in Rocket.
This commit is contained in:
parent
592e441de5
commit
2537a1164d
|
@ -206,6 +206,33 @@ impl<'r> Outcome<'r> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the `Outcome` of response to `req` from `responder`.
|
||||||
|
///
|
||||||
|
/// If the responder returns `Ok`, an outcome of `Success` is
|
||||||
|
/// returned with the response. If the responder returns `Err`, an
|
||||||
|
/// outcome of `Failure` is returned with the status code.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::{Request, Data};
|
||||||
|
/// use rocket::handler::Outcome;
|
||||||
|
///
|
||||||
|
/// fn str_responder(req: &Request, _: Data) -> Outcome<'static> {
|
||||||
|
/// Outcome::from(req, "Hello, world!")
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn try_from<T, E>(req: &Request<'_>, result: Result<T, E>) -> Outcome<'r>
|
||||||
|
where T: Responder<'r>, E: std::fmt::Debug
|
||||||
|
{
|
||||||
|
let responder = result.map_err(crate::response::Debug);
|
||||||
|
match responder.respond_to(req) {
|
||||||
|
Ok(response) => outcome::Outcome::Success(response),
|
||||||
|
Err(status) => outcome::Outcome::Failure(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the `Outcome` of response to `req` from `responder`.
|
/// Return the `Outcome` of response to `req` from `responder`.
|
||||||
///
|
///
|
||||||
/// If the responder returns `Ok`, an outcome of `Success` is
|
/// If the responder returns `Ok`, an outcome of `Success` is
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#![feature(specialization)]
|
|
||||||
#![feature(try_trait)]
|
#![feature(try_trait)]
|
||||||
#![feature(proc_macro_hygiene)]
|
#![feature(proc_macro_hygiene)]
|
||||||
#![feature(crate_visibility_modifier)]
|
#![feature(crate_visibility_modifier)]
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
use crate::request::Request;
|
||||||
|
use crate::response::{self, Response, Responder};
|
||||||
|
use crate::http::Status;
|
||||||
|
|
||||||
|
use yansi::Paint;
|
||||||
|
|
||||||
|
/// Debug prints the internal value before responding with a 500 error.
|
||||||
|
///
|
||||||
|
/// This value exists primarily to allow handler return types that would not
|
||||||
|
/// otherwise implement [`Responder`]. It is typically used in conjunction with
|
||||||
|
/// `Result<T, E>` where `E` implements `Debug` but not `Responder`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// Because of the generic `From<E>` implementation for `Debug<E>`, conversions
|
||||||
|
/// from `Result<T, E>` to `Result<T, Debug<E>>` through `?` occur
|
||||||
|
/// automatically:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #![feature(proc_macro_hygiene)]
|
||||||
|
/// use std::io::{self, Read};
|
||||||
|
///
|
||||||
|
/// # use rocket::post;
|
||||||
|
/// use rocket::Data;
|
||||||
|
/// use rocket::response::Debug;
|
||||||
|
///
|
||||||
|
/// #[post("/", format = "plain", data = "<data>")]
|
||||||
|
/// fn post(data: Data) -> Result<String, Debug<io::Error>> {
|
||||||
|
/// let mut name = String::with_capacity(32);
|
||||||
|
/// data.open().take(32).read_to_string(&mut name)?;
|
||||||
|
/// Ok(name)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// It is also possible to map the error directly to `Debug` via
|
||||||
|
/// [`Result::map_err()`]:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #![feature(proc_macro_hygiene)]
|
||||||
|
/// use std::string::FromUtf8Error;
|
||||||
|
///
|
||||||
|
/// # use rocket::get;
|
||||||
|
/// use rocket::response::Debug;
|
||||||
|
///
|
||||||
|
/// #[get("/")]
|
||||||
|
/// fn rand_str() -> Result<String, Debug<FromUtf8Error>> {
|
||||||
|
/// # /*
|
||||||
|
/// let bytes: Vec<u8> = random_bytes();
|
||||||
|
/// # */
|
||||||
|
/// # let bytes: Vec<u8> = vec![];
|
||||||
|
/// String::from_utf8(bytes).map_err(Debug)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Debug<E>(pub E);
|
||||||
|
|
||||||
|
impl<E> From<E> for Debug<E> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(e: E) -> Self {
|
||||||
|
Debug(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r, E: std::fmt::Debug> Responder<'r> for Debug<E> {
|
||||||
|
fn respond_to(self, _: &Request<'_>) -> response::Result<'r> {
|
||||||
|
warn_!("Debug: {:?}", Paint::default(self.0));
|
||||||
|
warn_!("Debug always responds with {}.", Status::InternalServerError);
|
||||||
|
Response::build().status(Status::InternalServerError).ok()
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ mod redirect;
|
||||||
mod named_file;
|
mod named_file;
|
||||||
mod stream;
|
mod stream;
|
||||||
mod response;
|
mod response;
|
||||||
|
mod debug;
|
||||||
|
|
||||||
crate mod flash;
|
crate mod flash;
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ pub use self::redirect::Redirect;
|
||||||
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::debug::Debug;
|
||||||
#[doc(inline)] pub use self::content::Content;
|
#[doc(inline)] pub use self::content::Content;
|
||||||
|
|
||||||
/// Type alias for the `Result` of a `Responder::respond` call.
|
/// Type alias for the `Result` of a `Responder::respond` call.
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Cursor, BufReader};
|
use std::io::{Cursor, BufReader};
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use crate::http::{Status, ContentType, StatusClass};
|
use crate::http::{Status, ContentType, StatusClass};
|
||||||
use crate::response::{self, Response, Body};
|
use crate::response::{self, Response, Body};
|
||||||
|
@ -268,28 +267,9 @@ impl<'r, R: Responder<'r>> Responder<'r> for Option<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If `self` is `Ok`, responds with the wrapped `Responder`. Otherwise prints
|
|
||||||
/// an error message with the `Err` value returns an `Err` of
|
|
||||||
/// `Status::InternalServerError`.
|
|
||||||
#[deprecated(since = "0.4.3")]
|
|
||||||
impl<'r, R: Responder<'r>, E: fmt::Debug> Responder<'r> for Result<R, E> {
|
|
||||||
default fn respond_to(self, req: &Request<'_>) -> response::Result<'r> {
|
|
||||||
self.map(|r| r.respond_to(req)).unwrap_or_else(|e| {
|
|
||||||
error_!("Response was a non-`Responder` `Err`: {:?}.", e);
|
|
||||||
warn_!("This `Responder` implementation has been deprecated.");
|
|
||||||
warn_!(
|
|
||||||
"In Rocket v0.5, `Result<T, E>` implements `Responder` only if \
|
|
||||||
`E` implements `Responder`. For the previous behavior, use \
|
|
||||||
`Result<T, Debug<E>>` where `Debug` is `rocket::response::Debug`."
|
|
||||||
);
|
|
||||||
Err(Status::InternalServerError)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Responds with the wrapped `Responder` in `self`, whether it is `Ok` or
|
/// Responds with the wrapped `Responder` in `self`, whether it is `Ok` or
|
||||||
/// `Err`.
|
/// `Err`.
|
||||||
impl<'r, R: Responder<'r>, E: Responder<'r> + fmt::Debug> Responder<'r> for Result<R, E> {
|
impl<'r, R: Responder<'r>, E: Responder<'r>> Responder<'r> for Result<R, E> {
|
||||||
fn respond_to(self, req: &Request<'_>) -> response::Result<'r> {
|
fn respond_to(self, req: &Request<'_>) -> response::Result<'r> {
|
||||||
match self {
|
match self {
|
||||||
Ok(responder) => responder.respond_to(req),
|
Ok(responder) => responder.respond_to(req),
|
||||||
|
|
|
@ -7,7 +7,12 @@
|
||||||
|
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
|
|
||||||
use rocket::{Request, response::content, data::Data};
|
use rocket::{Request, data::Data};
|
||||||
|
use rocket::response::{Debug, content::{Json, Html}};
|
||||||
|
|
||||||
|
// NOTE: This example explicitly uses the `Json` type from `response::content`
|
||||||
|
// for demonstration purposes. In a real application, _always_ prefer to use
|
||||||
|
// `rocket_contrib::json::Json` instead!
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct Person {
|
struct Person {
|
||||||
|
@ -20,10 +25,10 @@ struct Person {
|
||||||
// the route attribute. Note: if this was a real application, we'd use
|
// the route attribute. Note: if this was a real application, we'd use
|
||||||
// `rocket_contrib`'s built-in JSON support and return a `JsonValue` instead.
|
// `rocket_contrib`'s built-in JSON support and return a `JsonValue` instead.
|
||||||
#[get("/<name>/<age>", format = "json")]
|
#[get("/<name>/<age>", format = "json")]
|
||||||
fn get_hello(name: String, age: u8) -> content::Json<String> {
|
fn get_hello(name: String, age: u8) -> Json<String> {
|
||||||
// In a real application, we'd use the JSON contrib type.
|
// NOTE: In a real application, we'd use `rocket_contrib::json::Json`.
|
||||||
let person = Person { name: name, age: age, };
|
let person = Person { name: name, age: age, };
|
||||||
content::Json(serde_json::to_string(&person).unwrap())
|
Json(serde_json::to_string(&person).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// In a `POST` request and all other payload supporting request types, the
|
// In a `POST` request and all other payload supporting request types, the
|
||||||
|
@ -33,15 +38,16 @@ fn get_hello(name: String, age: u8) -> content::Json<String> {
|
||||||
// In a real application, we wouldn't use `serde_json` directly; instead, we'd
|
// In a real application, we wouldn't use `serde_json` directly; instead, we'd
|
||||||
// use `contrib::Json` to automatically serialize a type into JSON.
|
// use `contrib::Json` to automatically serialize a type into JSON.
|
||||||
#[post("/<age>", format = "plain", data = "<name_data>")]
|
#[post("/<age>", format = "plain", data = "<name_data>")]
|
||||||
fn post_hello(age: u8, name_data: Data) -> io::Result<content::Json<String>> {
|
fn post_hello(age: u8, name_data: Data) -> Result<Json<String>, Debug<io::Error>> {
|
||||||
let mut name = String::with_capacity(32);
|
let mut name = String::with_capacity(32);
|
||||||
name_data.open().take(32).read_to_string(&mut name)?;
|
name_data.open().take(32).read_to_string(&mut name)?;
|
||||||
let person = Person { name: name, age: age, };
|
let person = Person { name: name, age: age, };
|
||||||
Ok(content::Json(serde_json::to_string(&person).unwrap()))
|
// NOTE: In a real application, we'd use `rocket_contrib::json::Json`.
|
||||||
|
Ok(Json(serde_json::to_string(&person).expect("valid JSON")))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[catch(404)]
|
#[catch(404)]
|
||||||
fn not_found(request: &Request<'_>) -> content::Html<String> {
|
fn not_found(request: &Request<'_>) -> Html<String> {
|
||||||
let html = match request.format() {
|
let html = match request.format() {
|
||||||
Some(ref mt) if !mt.is_json() && !mt.is_plain() => {
|
Some(ref mt) if !mt.is_json() && !mt.is_plain() => {
|
||||||
format!("<p>'{}' requests are not supported.</p>", mt)
|
format!("<p>'{}' requests are not supported.</p>", mt)
|
||||||
|
@ -51,7 +57,7 @@ fn not_found(request: &Request<'_>) -> content::Html<String> {
|
||||||
request.uri())
|
request.uri())
|
||||||
};
|
};
|
||||||
|
|
||||||
content::Html(html)
|
Html(html)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
#[macro_use] extern crate rocket;
|
#[macro_use] extern crate rocket;
|
||||||
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
use rocket::request::{Form, FormError, FormDataError};
|
use rocket::request::{Form, FormError, FormDataError};
|
||||||
use rocket::response::NamedFile;
|
use rocket::response::NamedFile;
|
||||||
use rocket::http::RawStr;
|
use rocket::http::RawStr;
|
||||||
|
@ -39,8 +37,8 @@ fn sink(sink: Result<Form<FormInput<'_>>, FormError<'_>>) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn index() -> io::Result<NamedFile> {
|
fn index() -> Option<NamedFile> {
|
||||||
NamedFile::open("static/index.html")
|
NamedFile::open("static/index.html").ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rocket() -> rocket::Rocket {
|
fn rocket() -> rocket::Rocket {
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
use rocket::response::NamedFile;
|
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use rocket::response::NamedFile;
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub fn index() -> io::Result<NamedFile> {
|
pub fn index() -> Option<NamedFile> {
|
||||||
NamedFile::open("static/index.html")
|
NamedFile::open("static/index.html").ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/<file..>", rank = 2)]
|
#[get("/<file..>", rank = 2)]
|
||||||
pub fn files(file: PathBuf) -> io::Result<NamedFile> {
|
pub fn files(file: PathBuf) -> Option<NamedFile> {
|
||||||
NamedFile::open(Path::new("static/").join(file))
|
NamedFile::open(Path::new("static/").join(file)).ok()
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ fn echo_url<'r>(req: &'r Request, _: Data) -> Outcome<'r> {
|
||||||
.and_then(|res| res.ok())
|
.and_then(|res| res.ok())
|
||||||
.into_outcome(Status::BadRequest)?;
|
.into_outcome(Status::BadRequest)?;
|
||||||
|
|
||||||
Outcome::from(req, RawStr::from_str(param).url_decode())
|
Outcome::try_from(req, RawStr::from_str(param).url_decode())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> {
|
fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use rocket::Data;
|
use rocket::Data;
|
||||||
use rocket::response::content;
|
use rocket::response::{content, Debug};
|
||||||
|
|
||||||
use crate::paste_id::PasteID;
|
use crate::paste_id::PasteID;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ const HOST: &str = "http://localhost:8000";
|
||||||
const ID_LENGTH: usize = 3;
|
const ID_LENGTH: usize = 3;
|
||||||
|
|
||||||
#[post("/", data = "<paste>")]
|
#[post("/", data = "<paste>")]
|
||||||
fn upload(paste: Data) -> io::Result<String> {
|
fn upload(paste: Data) -> Result<String, Debug<io::Error>> {
|
||||||
let id = PasteID::new(ID_LENGTH);
|
let id = PasteID::new(ID_LENGTH);
|
||||||
let filename = format!("upload/{id}", id = id);
|
let filename = format!("upload/{id}", id = id);
|
||||||
let url = format!("{host}/{id}\n", host = HOST, id = id);
|
let url = format!("{host}/{id}\n", host = HOST, id = id);
|
||||||
|
|
|
@ -7,7 +7,9 @@ use rusqlite::types::ToSql;
|
||||||
#[cfg(test)] mod tests;
|
#[cfg(test)] mod tests;
|
||||||
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use rocket::{Rocket, State};
|
|
||||||
|
use rocket::{Rocket, State, response::Debug};
|
||||||
|
|
||||||
use rusqlite::{Connection, Error};
|
use rusqlite::{Connection, Error};
|
||||||
|
|
||||||
type DbConn = Mutex<Connection>;
|
type DbConn = Mutex<Connection>;
|
||||||
|
@ -25,11 +27,12 @@ fn init_database(conn: &Connection) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn hello(db_conn: State<'_, DbConn>) -> Result<String, Error> {
|
fn hello(db_conn: State<'_, DbConn>) -> Result<String, Debug<Error>> {
|
||||||
db_conn.lock()
|
db_conn.lock()
|
||||||
.expect("db connection lock")
|
.expect("db connection lock")
|
||||||
.query_row("SELECT name FROM entries WHERE id = 0",
|
.query_row("SELECT name FROM entries WHERE id = 0",
|
||||||
&[] as &[&dyn ToSql], |row| { row.get(0) })
|
&[] as &[&dyn ToSql], |row| { row.get(0) })
|
||||||
|
.map_err(Debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rocket() -> Rocket {
|
fn rocket() -> Rocket {
|
||||||
|
|
|
@ -5,11 +5,13 @@
|
||||||
#[cfg(test)] mod tests;
|
#[cfg(test)] mod tests;
|
||||||
|
|
||||||
use std::{io, env};
|
use std::{io, env};
|
||||||
use rocket::Data;
|
use rocket::{Data, response::Debug};
|
||||||
|
|
||||||
#[post("/upload", format = "plain", data = "<data>")]
|
#[post("/upload", format = "plain", data = "<data>")]
|
||||||
fn upload(data: Data) -> io::Result<String> {
|
fn upload(data: Data) -> Result<String, Debug<io::Error>> {
|
||||||
data.stream_to_file(env::temp_dir().join("upload.txt")).map(|n| n.to_string())
|
data.stream_to_file(env::temp_dir().join("upload.txt"))
|
||||||
|
.map(|n| n.to_string())
|
||||||
|
.map_err(Debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
use rocket::response::{content, Stream};
|
use rocket::response::{content, Stream};
|
||||||
|
|
||||||
use std::io::{self, repeat, Repeat, Read, Take};
|
use std::io::{repeat, Repeat, Read, Take};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
||||||
type LimitedRepeat = Take<Repeat>;
|
type LimitedRepeat = Take<Repeat>;
|
||||||
|
@ -20,8 +20,8 @@ fn root() -> content::Plain<Stream<LimitedRepeat>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/big_file")]
|
#[get("/big_file")]
|
||||||
fn file() -> io::Result<Stream<File>> {
|
fn file() -> Option<Stream<File>> {
|
||||||
File::open(FILENAME).map(|file| Stream::from(file))
|
File::open(FILENAME).map(|file| Stream::from(file)).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rocket() -> rocket::Rocket {
|
fn rocket() -> rocket::Rocket {
|
||||||
|
|
Loading…
Reference in New Issue