2016-10-04 00:09:13 +00:00
|
|
|
use std::fmt;
|
|
|
|
use std::convert::From;
|
2016-08-23 03:34:22 +00:00
|
|
|
|
2017-06-02 04:44:31 +00:00
|
|
|
use yansi::Color::*;
|
2016-08-23 03:34:22 +00:00
|
|
|
|
2016-10-04 00:09:13 +00:00
|
|
|
use codegen::StaticRouteInfo;
|
|
|
|
use handler::Handler;
|
2017-03-28 07:12:59 +00:00
|
|
|
use http::{Method, MediaType};
|
2017-09-13 09:12:48 +00:00
|
|
|
use http::uri::Uri;
|
2016-03-21 04:27:17 +00:00
|
|
|
|
2017-03-28 07:12:59 +00:00
|
|
|
/// A route: a method, its handler, path, rank, and format/media type.
|
2016-03-21 04:27:17 +00:00
|
|
|
pub struct Route {
|
2017-08-19 01:37:25 +00:00
|
|
|
/// The name of this route, if one was given.
|
|
|
|
pub name: Option<&'static str>,
|
2016-11-04 13:35:04 +00:00
|
|
|
/// The method this route matches against.
|
2016-04-02 07:51:40 +00:00
|
|
|
pub method: Method,
|
2017-06-30 20:17:40 +00:00
|
|
|
/// The function that should be called when the route matches.
|
2016-04-03 10:36:30 +00:00
|
|
|
pub handler: Handler,
|
2017-05-27 02:48:50 +00:00
|
|
|
/// The base mount point of this `Route`.
|
2017-09-13 09:12:48 +00:00
|
|
|
pub base: Uri<'static>,
|
2017-06-12 22:08:34 +00:00
|
|
|
/// The uri (in Rocket format) that should be matched against. This uri
|
2017-05-27 02:48:50 +00:00
|
|
|
/// already includes the base mount point.
|
2017-09-13 09:12:48 +00:00
|
|
|
pub uri: Uri<'static>,
|
2016-11-04 13:35:04 +00:00
|
|
|
/// The rank of this route. Lower ranks have higher priorities.
|
|
|
|
pub rank: isize,
|
2017-06-30 20:17:40 +00:00
|
|
|
/// The media type this route matches against, if any.
|
2017-03-28 07:12:59 +00:00
|
|
|
pub format: Option<MediaType>,
|
2016-03-22 05:04:39 +00:00
|
|
|
}
|
|
|
|
|
2017-03-27 10:52:26 +00:00
|
|
|
#[inline(always)]
|
2017-09-13 09:12:48 +00:00
|
|
|
fn default_rank(uri: &Uri) -> isize {
|
2017-03-27 10:52:26 +00:00
|
|
|
// static path, query = -4; static path, no query = -3
|
|
|
|
// dynamic path, query = -2; dynamic path, no query = -1
|
|
|
|
match (!uri.path().contains('<'), uri.query().is_some()) {
|
|
|
|
(true, true) => -4,
|
|
|
|
(true, false) => -3,
|
|
|
|
(false, true) => -2,
|
|
|
|
(false, false) => -1,
|
|
|
|
}
|
2016-08-27 02:03:21 +00:00
|
|
|
}
|
|
|
|
|
2016-03-21 04:27:17 +00:00
|
|
|
impl Route {
|
2017-06-30 20:17:40 +00:00
|
|
|
/// Creates a new route with the given method, path, and handler with a base
|
|
|
|
/// of `/`.
|
2016-11-04 13:35:04 +00:00
|
|
|
///
|
2017-06-30 20:17:40 +00:00
|
|
|
/// # Ranking
|
|
|
|
///
|
2018-07-12 03:44:09 +00:00
|
|
|
/// The route's rank is set so that routes with static paths are ranked
|
|
|
|
/// higher than routes with dynamic paths, and routes with query strings
|
|
|
|
/// are ranked higher than routes without query strings. This default ranking
|
2017-06-30 20:17:40 +00:00
|
|
|
/// is summarized by the table below:
|
|
|
|
///
|
|
|
|
/// | static path | query | rank |
|
|
|
|
/// |-------------|-------|------|
|
|
|
|
/// | yes | yes | -4 |
|
|
|
|
/// | yes | no | -3 |
|
|
|
|
/// | no | yes | -2 |
|
|
|
|
/// | no | no | -1 |
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use rocket::{Request, Route, Data};
|
|
|
|
/// use rocket::handler::Outcome;
|
|
|
|
/// use rocket::http::Method;
|
|
|
|
///
|
|
|
|
/// fn handler<'r>(request: &'r Request, _data: Data) -> Outcome<'r> {
|
|
|
|
/// Outcome::from(request, "Hello, world!")
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// // this is a rank -3 route matching requests to `GET /`
|
|
|
|
/// let index = Route::new(Method::Get, "/", handler);
|
|
|
|
///
|
|
|
|
/// // this is a rank -4 route matching requests to `GET /?<name>`
|
|
|
|
/// let index_name = Route::new(Method::Get, "/?<name>", handler);
|
|
|
|
///
|
|
|
|
/// // this is a rank -1 route matching requests to `GET /<name>`
|
|
|
|
/// let name = Route::new(Method::Get, "/<name>", handler);
|
|
|
|
/// ```
|
2016-11-04 13:35:04 +00:00
|
|
|
pub fn new<S>(m: Method, path: S, handler: Handler) -> Route
|
2016-09-30 22:20:11 +00:00
|
|
|
where S: AsRef<str>
|
|
|
|
{
|
2017-09-13 09:12:48 +00:00
|
|
|
let uri = Uri::from(path.as_ref().to_string());
|
2016-08-23 03:38:39 +00:00
|
|
|
Route {
|
2017-08-19 01:37:25 +00:00
|
|
|
name: None,
|
2016-08-23 03:38:39 +00:00
|
|
|
method: m,
|
|
|
|
handler: handler,
|
2017-03-27 10:52:26 +00:00
|
|
|
rank: default_rank(&uri),
|
2017-09-13 09:12:48 +00:00
|
|
|
base: Uri::from("/"),
|
2017-06-12 22:08:34 +00:00
|
|
|
uri: uri,
|
2017-02-01 11:12:24 +00:00
|
|
|
format: None,
|
2016-08-23 03:38:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-30 20:17:40 +00:00
|
|
|
/// Creates a new route with the given rank, method, path, and handler with
|
|
|
|
/// a base of `/`.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use rocket::{Request, Route, Data};
|
|
|
|
/// use rocket::handler::Outcome;
|
|
|
|
/// use rocket::http::Method;
|
|
|
|
///
|
|
|
|
/// fn handler<'r>(request: &'r Request, _data: Data) -> Outcome<'r> {
|
|
|
|
/// Outcome::from(request, "Hello, world!")
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// // this is a rank 1 route matching requests to `GET /`
|
|
|
|
/// let index = Route::ranked(1, Method::Get, "/", handler);
|
|
|
|
/// ```
|
2017-06-12 22:08:34 +00:00
|
|
|
pub fn ranked<S>(rank: isize, m: Method, uri: S, handler: Handler) -> Route
|
2016-09-30 22:20:11 +00:00
|
|
|
where S: AsRef<str>
|
|
|
|
{
|
2016-04-02 08:46:41 +00:00
|
|
|
Route {
|
2017-08-19 01:37:25 +00:00
|
|
|
name: None,
|
2016-04-02 08:46:41 +00:00
|
|
|
method: m,
|
2016-11-04 13:35:04 +00:00
|
|
|
handler: handler,
|
2017-09-13 09:12:48 +00:00
|
|
|
base: Uri::from("/"),
|
|
|
|
uri: Uri::from(uri.as_ref().to_string()),
|
2016-11-04 13:35:04 +00:00
|
|
|
rank: rank,
|
2017-02-01 11:12:24 +00:00
|
|
|
format: None,
|
2016-03-21 04:27:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-30 20:17:40 +00:00
|
|
|
/// Retrieves the path of the base mount point of this route as an `&str`.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use rocket::{Request, Route, Data};
|
|
|
|
/// use rocket::handler::Outcome;
|
|
|
|
/// use rocket::http::Method;
|
|
|
|
///
|
|
|
|
/// fn handler<'r>(request: &'r Request, _data: Data) -> Outcome<'r> {
|
|
|
|
/// Outcome::from(request, "Hello, world!")
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// let mut index = Route::ranked(1, Method::Get, "/", handler);
|
|
|
|
/// assert_eq!(index.base(), "/");
|
|
|
|
/// assert_eq!(index.base.path(), "/");
|
|
|
|
/// ```
|
2017-06-12 22:08:34 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn base(&self) -> &str {
|
|
|
|
self.base.path()
|
|
|
|
}
|
|
|
|
|
2017-05-27 02:48:50 +00:00
|
|
|
/// Sets the base mount point of the route. Does not update the rank or any
|
|
|
|
/// other parameters.
|
2017-06-30 20:17:40 +00:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use rocket::{Request, Route, Data};
|
|
|
|
/// use rocket::handler::Outcome;
|
|
|
|
/// use rocket::http::Method;
|
|
|
|
///
|
|
|
|
/// fn handler<'r>(request: &'r Request, _data: Data) -> Outcome<'r> {
|
|
|
|
/// Outcome::from(request, "Hello, world!")
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// let mut index = Route::ranked(1, Method::Get, "/", handler);
|
|
|
|
/// assert_eq!(index.base(), "/");
|
|
|
|
/// assert_eq!(index.base.path(), "/");
|
|
|
|
///
|
|
|
|
/// index.set_base("/hi");
|
|
|
|
/// assert_eq!(index.base(), "/hi");
|
|
|
|
/// assert_eq!(index.base.path(), "/hi");
|
|
|
|
/// ```
|
2017-05-27 02:48:50 +00:00
|
|
|
pub fn set_base<S>(&mut self, path: S) where S: AsRef<str> {
|
2017-09-13 09:12:48 +00:00
|
|
|
self.base = Uri::from(path.as_ref().to_string());
|
2017-05-27 02:48:50 +00:00
|
|
|
}
|
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
/// Sets the path of the route. Does not update the rank or any other
|
|
|
|
/// parameters.
|
2017-06-30 20:17:40 +00:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use rocket::{Request, Route, Data};
|
|
|
|
/// use rocket::handler::Outcome;
|
|
|
|
/// use rocket::http::Method;
|
|
|
|
///
|
|
|
|
/// fn handler<'r>(request: &'r Request, _data: Data) -> Outcome<'r> {
|
|
|
|
/// Outcome::from(request, "Hello, world!")
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// let mut index = Route::ranked(1, Method::Get, "/", handler);
|
|
|
|
/// assert_eq!(index.uri.path(), "/");
|
|
|
|
///
|
|
|
|
/// index.set_uri("/hello");
|
|
|
|
/// assert_eq!(index.uri.path(), "/hello");
|
|
|
|
/// ```
|
2017-06-12 22:08:34 +00:00
|
|
|
pub fn set_uri<S>(&mut self, uri: S) where S: AsRef<str> {
|
2017-09-13 09:12:48 +00:00
|
|
|
self.uri = Uri::from(uri.as_ref().to_string());
|
2016-03-21 04:27:17 +00:00
|
|
|
}
|
|
|
|
|
2016-03-22 05:04:39 +00:00
|
|
|
// FIXME: Decide whether a component has to be fully variable or not. That
|
2016-03-28 09:34:09 +00:00
|
|
|
// is, whether you can have: /a<a>b/ or even /<a>:<b>/
|
2016-03-22 05:04:39 +00:00
|
|
|
// TODO: Don't return a Vec...take in an &mut [&'a str] (no alloc!)
|
2016-11-04 13:35:04 +00:00
|
|
|
/// Given a URI, returns a vector of slices of that URI corresponding to the
|
|
|
|
/// dynamic segments in this route.
|
2017-09-13 09:12:48 +00:00
|
|
|
pub(crate) fn get_param_indexes(&self, uri: &Uri) -> Vec<(usize, usize)> {
|
2017-06-12 22:08:34 +00:00
|
|
|
let route_segs = self.uri.segments();
|
2016-09-08 07:02:17 +00:00
|
|
|
let uri_segs = uri.segments();
|
2016-12-31 05:51:23 +00:00
|
|
|
let start_addr = uri.path().as_ptr() as usize;
|
2016-03-22 05:04:39 +00:00
|
|
|
|
2017-06-12 22:08:34 +00:00
|
|
|
let mut result = Vec::with_capacity(self.uri.segment_count());
|
2016-09-08 07:02:17 +00:00
|
|
|
for (route_seg, uri_seg) in route_segs.zip(uri_segs) {
|
2016-12-31 05:51:23 +00:00
|
|
|
let i = (uri_seg.as_ptr() as usize) - start_addr;
|
2016-09-30 22:20:11 +00:00
|
|
|
if route_seg.ends_with("..>") {
|
2016-12-31 05:51:23 +00:00
|
|
|
result.push((i, uri.path().len()));
|
2016-09-08 07:02:17 +00:00
|
|
|
break;
|
2016-09-30 22:20:11 +00:00
|
|
|
} else if route_seg.ends_with('>') {
|
2016-12-16 11:07:23 +00:00
|
|
|
let j = i + uri_seg.len();
|
|
|
|
result.push((i, j));
|
2016-03-22 05:04:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
2016-03-21 04:27:17 +00:00
|
|
|
}
|
|
|
|
|
2016-10-08 06:20:49 +00:00
|
|
|
impl Clone for Route {
|
|
|
|
fn clone(&self) -> Route {
|
|
|
|
Route {
|
2017-08-19 01:37:25 +00:00
|
|
|
name: self.name,
|
2016-10-08 06:20:49 +00:00
|
|
|
method: self.method,
|
|
|
|
handler: self.handler,
|
|
|
|
rank: self.rank,
|
2017-05-27 02:48:50 +00:00
|
|
|
base: self.base.clone(),
|
2017-06-12 22:08:34 +00:00
|
|
|
uri: self.uri.clone(),
|
2016-12-31 02:15:28 +00:00
|
|
|
format: self.format.clone(),
|
2016-10-08 06:20:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-21 09:22:22 +00:00
|
|
|
impl fmt::Display for Route {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2017-06-12 22:08:34 +00:00
|
|
|
write!(f, "{} {}", Green.paint(&self.method), Blue.paint(&self.uri))?;
|
2016-08-26 08:55:11 +00:00
|
|
|
|
2016-08-27 12:10:29 +00:00
|
|
|
if self.rank > 1 {
|
|
|
|
write!(f, " [{}]", White.paint(&self.rank))?;
|
|
|
|
}
|
|
|
|
|
2017-02-01 11:12:24 +00:00
|
|
|
if let Some(ref format) = self.format {
|
|
|
|
write!(f, " {}", Yellow.paint(format))?;
|
2016-08-26 08:55:11 +00:00
|
|
|
}
|
2017-02-01 11:12:24 +00:00
|
|
|
|
2017-08-19 01:37:25 +00:00
|
|
|
if let Some(name) = self.name {
|
|
|
|
write!(f, " {}{}{}",
|
|
|
|
Cyan.paint("("), Purple.paint(name), Cyan.paint(")"))?;
|
|
|
|
}
|
|
|
|
|
2017-02-01 11:12:24 +00:00
|
|
|
Ok(())
|
2016-03-21 09:22:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-08 07:02:17 +00:00
|
|
|
impl fmt::Debug for Route {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
<Route as fmt::Display>::fmt(self, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
#[doc(hidden)]
|
2016-04-03 11:25:37 +00:00
|
|
|
impl<'a> From<&'a StaticRouteInfo> for Route {
|
|
|
|
fn from(info: &'a StaticRouteInfo) -> Route {
|
2016-08-26 08:55:11 +00:00
|
|
|
let mut route = Route::new(info.method, info.path, info.handler);
|
2017-02-01 11:12:24 +00:00
|
|
|
route.format = info.format.clone();
|
2017-08-19 01:37:25 +00:00
|
|
|
route.name = Some(info.name);
|
2016-08-27 12:10:29 +00:00
|
|
|
if let Some(rank) = info.rank {
|
|
|
|
route.rank = rank;
|
|
|
|
}
|
|
|
|
|
2016-08-26 08:55:11 +00:00
|
|
|
route
|
2016-04-03 11:25:37 +00:00
|
|
|
}
|
|
|
|
}
|