mirror of https://github.com/rwf2/Rocket.git
Document the Rocket type. Add expect method to Outcome. Add custom method to Rocket.
This commit is contained in:
parent
5b8b41bcd8
commit
d7353c8c2d
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue