Document the Rocket type. Add expect method to Outcome. Add custom method to Rocket.

This commit is contained in:
Sergio Benitez 2016-11-04 14:35:04 +01:00
parent 5b8b41bcd8
commit d7353c8c2d
5 changed files with 224 additions and 44 deletions

View File

@ -8,6 +8,7 @@ use std::fmt;
use term_painter::ToStyle;
use term_painter::Color::*;
/// An error catching route.
pub struct Catcher {
pub code: u16,
handler: ErrorHandler,
@ -15,23 +16,32 @@ pub struct Catcher {
}
impl Catcher {
/// Creates a catcher for the given status code using the given error
/// handler.
#[inline(always)]
pub fn new(code: u16, handler: ErrorHandler) -> Catcher {
Catcher { code: code, handler: handler, is_default: false }
}
#[doc(hidden)]
#[inline(always)]
pub fn handle<'r>(&self, err: Error, request: &'r Request) -> Response<'r> {
(self.handler)(err, request)
}
#[inline(always)]
fn new_default(code: u16, handler: ErrorHandler) -> Catcher {
Catcher { code: code, handler: handler, is_default: true, }
}
#[doc(hidden)]
#[inline(always)]
pub fn is_default(&self) -> bool {
self.is_default
}
}
#[doc(hidden)]
impl<'a> From<&'a StaticCatchInfo> for Catcher {
fn from(info: &'a StaticCatchInfo) -> Catcher {
Catcher::new(info.code, info.handler)

View File

@ -137,7 +137,14 @@ pub use error::Error;
pub use catcher::Catcher;
pub use rocket::Rocket;
/// Alias to Rocket::ignite().
/// Alias to [Rocket::ignite()](/rocket/struct.Rocket.html#method.ignite).
/// Creates a new instance of `Rocket`.
pub fn ignite() -> Rocket {
Rocket::ignite()
}
/// Alias to [Rocket::custom()](/rocket/struct.Rocket.html#method.custom).
/// Creates a new instance of `Rocket` with a custom configuration.
pub fn custom(config: &config::Config) -> Rocket {
Rocket::custom(config)
}

View File

@ -129,6 +129,29 @@ impl<S, E, F> Outcome<S, E, F> {
}
}
/// Unwraps the Outcome, yielding the contents of a Success.
///
/// # Panics
///
/// If the value is not `Success`, panics with the given `message`.
///
/// # Examples
///
/// ```rust
/// # use rocket::outcome::Outcome;
/// # use rocket::outcome::Outcome::*;
/// #
/// let x: Outcome<i32, &str, usize> = Success(10);
/// assert_eq!(x.expect("success value"), 10);
/// ```
#[inline(always)]
pub fn expect(self, message: &str) -> S {
match self {
Success(val) => val,
_ => panic!("Outcome::expect() failed: {}", message)
}
}
/// Return true if this `Outcome` is a `Success`.
///
/// # Examples

View File

@ -6,8 +6,8 @@ use std::process;
use term_painter::Color::*;
use term_painter::ToStyle;
use config;
use logger;
use config::{self, Config};
use request::{Request, FormItems};
use data::Data;
use response::Responder;
@ -20,7 +20,7 @@ use http::{Method, StatusCode};
use http::hyper::{HyperRequest, FreshHyperResponse};
use http::hyper::{HyperServer, HyperHandler, HyperSetCookie, header};
/// The Rocket type used to mount routes and catchers and launch the
/// The main `Rocket` type: used to mount routes and catchers and launch the
/// application.
pub struct Rocket {
address: String,
@ -32,6 +32,11 @@ pub struct Rocket {
#[doc(hidden)]
impl HyperHandler for Rocket {
// This function tries to hide all of the Hyper-ness from Rocket. It
// essentially converts Hyper types into Rocket types, then calls the
// `dispatch` function, which knows nothing about Hyper. Because responding
// depends on the `HyperResponse` type, this function does the actual
// response processing.
fn handle<'h, 'k>(&self,
hyp_req: HyperRequest<'h, 'k>,
mut res: FreshHyperResponse<'h>) {
@ -115,6 +120,11 @@ impl Rocket {
}
}
/// Tries to find a `Responder` for a given `request`. It does this by
/// routing the request and calling the handler for each matching route
/// until one of the handlers returns success or failure. If a handler
/// returns a failure, or there are no matching handlers willing to accept
/// the request, this function returns an `Err` with the status code.
#[doc(hidden)]
pub fn dispatch<'r>(&self, request: &'r Request, mut data: Data)
-> Result<Box<Responder + 'r>, StatusCode> {
@ -143,7 +153,11 @@ impl Rocket {
Err(StatusCode::NotFound)
}
// Call when no route was found. Returns true if there was a response.
// Attempts to send a response to the client by using the catcher for the
// given status code. If no catcher is found (including the defaults), the
// 500 internal server error catcher is used. If the catcher fails to
// respond, this function returns `false`. It returns `true` if a response
// was sucessfully sent to the client.
#[doc(hidden)]
pub fn handle_error<'r>(&self,
code: StatusCode,
@ -153,7 +167,7 @@ impl Rocket {
let catcher = self.catchers.get(&code.to_u16()).unwrap_or_else(|| {
error_!("No catcher found for {}.", code);
warn_!("Using internal server error catcher.");
self.catchers.get(&500).expect("500 Catcher")
self.catchers.get(&500).expect("500 catcher should exist!")
});
if let Some(mut responder) = catcher.handle(Error::NoRoute, req).responder() {
@ -169,12 +183,129 @@ 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).unwrap()
responder.unwrap().respond(response).expect("default catcher failed")
}
true
}
/// Create a new `Rocket` application using the configuration information in
/// `Rocket.toml`. If the file does not exist or if there is an I/O error
/// reading the file, the defaults are used. See the
/// [config](/rocket/config/index.html) documentation for more information
/// on defaults.
///
/// This method is typically called through the `rocket::ignite` alias.
///
/// # Panics
///
/// If there is an error parsing the `Rocket.toml` file, this functions
/// prints a nice error message and then exits the process.
///
/// # Examples
///
/// ```rust
/// # {
/// rocket::ignite()
/// # };
/// ```
pub fn ignite() -> Rocket {
// Note: init() will exit the process under config errors.
let (config, initted) = config::init();
if initted {
logger::init(config.log_level);
}
Rocket::custom(config)
}
/// Creates a new `Rocket` application using the supplied custom
/// configuration information. Ignores the `Rocket.toml` file.
///
/// This method is typically called through the `rocket::custom` alias.
///
/// # Examples
///
/// ```rust
/// use rocket::config::{Config, Environment};
/// # use rocket::config::ConfigError;
///
/// # fn try_config() -> Result<(), ConfigError> {
/// let config = Config::default_for(Environment::active()?, "/custom")?
/// .address("1.2.3.4".into())
/// .port(9234);
///
/// let app = rocket::custom(&config);
/// # Ok(())
/// # }
/// ```
pub fn custom(config: &Config) -> Rocket {
logger::init(config.log_level);
info!("🔧 Configured for {}.", config.env);
info_!("listening: {}:{}",
White.paint(&config.address),
White.paint(&config.port));
info_!("logging: {:?}", White.paint(config.log_level));
info_!("session key: {}", White.paint(config.take_session_key().is_some()));
for (name, value) in config.extras() {
info_!("{} {}: {}", Yellow.paint("[extra]"), name, White.paint(value));
}
Rocket {
address: config.address.clone(),
port: config.port,
router: Router::new(),
default_catchers: catcher::defaults::get(),
catchers: catcher::defaults::get(),
}
}
/// Mounts all of the routes in the supplied vector at the given `base`
/// path. Mounting a route with path `path` at path `base` makes the route
/// available at `base/path`.
///
/// # Examples
///
/// Use the `routes!` macro to mount routes created using the code
/// generation facilities. Requests to the `/hello/world` URI will be
/// dispatched to the `hi` route.
///
/// ```rust
/// # #![feature(plugin)]
/// # #![plugin(rocket_codegen)]
/// # extern crate rocket;
/// #
/// #[get("/world")]
/// fn hi() -> &'static str {
/// "Hello!"
/// }
///
/// fn main() {
/// # if false { // We don't actually want to launch the server in an example.
/// rocket::ignite().mount("/hello", routes![hi])
/// # .launch()
/// # }
/// }
/// ```
///
/// Manually create a route named `hi` at path `"/world"` mounted at base
/// `"/hello"`. Requests to the `/hello/world` URI will be dispatched to the
/// `hi` route.
///
/// ```rust
/// use rocket::{Request, Response, Route, Data};
/// use rocket::http::Method::*;
///
/// fn hi(_: &Request, _: Data) -> Response<'static> {
/// Response::success("Hello!")
/// }
///
/// # if false { // We don't actually want to launch the server in an example.
/// rocket::ignite().mount("/hello", vec![Route::new(Get, "/world", hi)])
/// # :.launch()
/// # }
/// ```
pub fn mount(mut self, base: &str, routes: Vec<Route>) -> Self {
info!("🛰 {} '{}':", Magenta.paint("Mounting"), base);
for mut route in routes {
@ -188,6 +319,7 @@ impl Rocket {
self
}
/// Registers all of the catchers in the supplied vector.
pub fn catch(mut self, catchers: Vec<Catcher>) -> Self {
info!("👾 {}:", Magenta.paint("Catchers"));
for c in catchers {
@ -204,6 +336,22 @@ impl Rocket {
self
}
/// Starts the application server and begins listening for and dispatching
/// requests to mounted routes and catchers.
///
/// # Panics
///
/// If the server could not be started, this method prints the reason and
/// then exits the process.
///
/// # Examples
///
/// ```rust
/// # if false {
/// rocket::ignite().launch()
/// # }
/// ```
#[cfg(not(feature = "testing"))]
pub fn launch(self) {
if self.router.has_collisions() {
warn!("Route collisions detected!");
@ -226,29 +374,4 @@ impl Rocket {
server.handle(self).unwrap();
}
pub fn ignite() -> Rocket {
// Note: init() will exit the process under config errors.
let (config, initted) = config::init();
if initted {
logger::init(config.log_level);
}
info!("🔧 Configured for {}.", config.env);
info_!("listening: {}:{}",
White.paint(&config.address),
White.paint(&config.port));
info_!("logging: {:?}", White.paint(config.log_level));
info_!("session key: {}", White.paint(config.take_session_key().is_some()));
for (name, value) in config.extras() {
info_!("{} {}: {}", Yellow.paint("[extra]"), name, White.paint(value));
}
Rocket {
address: config.address.clone(),
port: config.port,
router: Router::new(),
default_catchers: catcher::defaults::get(),
catchers: catcher::defaults::get(),
}
}
}

View File

@ -12,11 +12,17 @@ use request::Request;
use http::{Method, ContentType};
use http::uri::{URI, URIBuf};
/// A route: a method, its handler, path, rank, and format/content type.
pub struct Route {
/// The method this route matches against.
pub method: Method,
/// A function that should be called when the route matches.
pub handler: Handler,
/// The path (in Rocket format) that should be matched against.
pub path: URIBuf,
pub rank: isize, // Lower ranks have higher priorities.
/// The rank of this route. Lower ranks have higher priorities.
pub rank: isize,
/// The Content-Type this route matches against.
pub content_type: ContentType,
}
@ -27,18 +33,10 @@ fn default_rank(path: &str) -> isize {
}
impl Route {
pub fn ranked<S>(rank: isize, m: Method, path: S, handler: Handler) -> Route
where S: AsRef<str>
{
Route {
method: m,
path: URIBuf::from(path.as_ref()),
handler: handler,
rank: rank,
content_type: ContentType::any(),
}
}
/// Creates a new route with the method, path, and handler.
///
/// The rank of the route will be `0` if the path contains no dynamic
/// segments, and `1` if it does.
pub fn new<S>(m: Method, path: S, handler: Handler) -> Route
where S: AsRef<str>
{
@ -51,6 +49,21 @@ impl Route {
}
}
/// Creates a new route with the given rank, method, path, and handler.
pub fn ranked<S>(rank: isize, m: Method, path: S, handler: Handler) -> Route
where S: AsRef<str>
{
Route {
method: m,
path: URIBuf::from(path.as_ref()),
handler: handler,
rank: rank,
content_type: ContentType::any(),
}
}
/// Sets the path of the route. Does not update the rank or any other
/// parameters.
pub fn set_path<S>(&mut self, path: S)
where S: AsRef<str>
{
@ -60,6 +73,9 @@ impl Route {
// FIXME: Decide whether a component has to be fully variable or not. That
// is, whether you can have: /a<a>b/ or even /<a>:<b>/
// TODO: Don't return a Vec...take in an &mut [&'a str] (no alloc!)
/// Given a URI, returns a vector of slices of that URI corresponding to the
/// dynamic segments in this route.
#[doc(hidden)]
pub fn get_params<'a>(&self, uri: URI<'a>) -> Vec<&'a str> {
let route_segs = self.path.as_uri().segments();
let uri_segs = uri.segments();
@ -113,6 +129,7 @@ impl fmt::Debug for Route {
}
}
#[doc(hidden)]
impl<'a> From<&'a StaticRouteInfo> for Route {
fn from(info: &'a StaticRouteInfo) -> Route {
let mut route = Route::new(info.method, info.path, info.handler);