2016-10-04 00:09:13 +00:00
|
|
|
use std::fmt;
|
|
|
|
use std::convert::From;
|
2016-08-23 03:34:22 +00:00
|
|
|
|
2016-10-04 00:25:27 +00:00
|
|
|
use super::Collider; // :D
|
|
|
|
|
2016-03-21 09:22:22 +00:00
|
|
|
use term_painter::ToStyle;
|
|
|
|
use term_painter::Color::*;
|
2016-08-23 03:34:22 +00:00
|
|
|
|
2016-10-04 00:09:13 +00:00
|
|
|
use codegen::StaticRouteInfo;
|
|
|
|
use handler::Handler;
|
2016-08-26 08:55:11 +00:00
|
|
|
use request::Request;
|
2016-10-04 00:09:13 +00:00
|
|
|
use http::{Method, ContentType};
|
2016-10-04 00:25:27 +00:00
|
|
|
use http::uri::{URI, URIBuf};
|
2016-03-21 04:27:17 +00:00
|
|
|
|
|
|
|
pub struct Route {
|
2016-04-02 07:51:40 +00:00
|
|
|
pub method: Method,
|
2016-04-03 10:36:30 +00:00
|
|
|
pub handler: Handler,
|
2016-04-02 07:51:40 +00:00
|
|
|
pub path: URIBuf,
|
2016-08-27 02:03:21 +00:00
|
|
|
pub rank: isize, // Lower ranks have higher priorities.
|
2016-08-23 03:34:22 +00:00
|
|
|
pub content_type: ContentType,
|
2016-03-22 05:04:39 +00:00
|
|
|
}
|
|
|
|
|
2016-08-27 02:03:21 +00:00
|
|
|
fn default_rank(path: &str) -> isize {
|
|
|
|
// The rank for a given path is 0 if it is a static route (it doesn't
|
|
|
|
// contain any dynamic <segmants>) or 1 if it is dynamic.
|
|
|
|
path.contains('<') as isize
|
|
|
|
}
|
|
|
|
|
2016-03-21 04:27:17 +00:00
|
|
|
impl Route {
|
2016-09-30 22:20:11 +00:00
|
|
|
pub fn ranked<S>(rank: isize, m: Method, path: S, handler: Handler) -> Route
|
|
|
|
where S: AsRef<str>
|
|
|
|
{
|
2016-08-23 03:38:39 +00:00
|
|
|
Route {
|
|
|
|
method: m,
|
|
|
|
path: URIBuf::from(path.as_ref()),
|
|
|
|
handler: handler,
|
|
|
|
rank: rank,
|
|
|
|
content_type: ContentType::any(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-30 22:20:11 +00:00
|
|
|
pub fn new<S>(m: Method, path: S, handler: Handler) -> Route
|
|
|
|
where S: AsRef<str>
|
|
|
|
{
|
2016-04-02 08:46:41 +00:00
|
|
|
Route {
|
|
|
|
method: m,
|
|
|
|
handler: handler,
|
2016-08-27 02:03:21 +00:00
|
|
|
rank: default_rank(path.as_ref()),
|
2016-04-03 10:36:30 +00:00
|
|
|
path: URIBuf::from(path.as_ref()),
|
2016-08-23 03:34:22 +00:00
|
|
|
content_type: ContentType::any(),
|
2016-03-21 04:27:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-30 22:20:11 +00:00
|
|
|
pub fn set_path<S>(&mut self, path: S)
|
|
|
|
where S: AsRef<str>
|
|
|
|
{
|
2016-04-03 10:36:30 +00:00
|
|
|
self.path = URIBuf::from(path.as_ref());
|
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-08-26 08:55:11 +00:00
|
|
|
pub fn get_params<'a>(&self, uri: URI<'a>) -> Vec<&'a str> {
|
2016-09-08 07:02:17 +00:00
|
|
|
let route_segs = self.path.segments();
|
|
|
|
let uri_segs = uri.segments();
|
2016-03-22 05:04:39 +00:00
|
|
|
|
2016-04-02 07:51:40 +00:00
|
|
|
let mut result = Vec::with_capacity(self.path.segment_count());
|
2016-09-08 07:02:17 +00:00
|
|
|
for (route_seg, uri_seg) in route_segs.zip(uri_segs) {
|
2016-09-30 22:20:11 +00:00
|
|
|
if route_seg.ends_with("..>") {
|
|
|
|
// FIXME: Here.
|
2016-09-08 07:02:17 +00:00
|
|
|
break;
|
2016-09-30 22:20:11 +00:00
|
|
|
} else if route_seg.ends_with('>') {
|
|
|
|
// FIXME: Here.
|
2016-04-02 07:51:40 +00:00
|
|
|
result.push(uri_seg);
|
2016-03-22 05:04:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
2016-03-21 04:27:17 +00:00
|
|
|
}
|
|
|
|
|
2016-03-21 09:22:22 +00:00
|
|
|
impl fmt::Display for Route {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2016-08-27 04:34:28 +00:00
|
|
|
write!(f, "{} {}", Green.paint(&self.method), Blue.paint(&self.path))?;
|
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))?;
|
|
|
|
}
|
|
|
|
|
2016-08-26 08:55:11 +00:00
|
|
|
if !self.content_type.is_any() {
|
2016-08-27 04:34:28 +00:00
|
|
|
write!(f, " {}", Yellow.paint(&self.content_type))
|
2016-08-26 08:55:11 +00:00
|
|
|
} else {
|
|
|
|
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-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);
|
2016-09-04 11:06:28 +00:00
|
|
|
route.content_type = info.format.clone().unwrap_or(ContentType::any());
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-21 04:27:17 +00:00
|
|
|
impl Collider for Route {
|
|
|
|
fn collides_with(&self, b: &Route) -> bool {
|
2016-09-08 07:02:17 +00:00
|
|
|
self.method == b.method
|
2016-08-26 08:55:11 +00:00
|
|
|
&& self.rank == b.rank
|
|
|
|
&& self.content_type.collides_with(&b.content_type)
|
2016-09-08 07:02:17 +00:00
|
|
|
&& self.path.as_uri().collides_with(&b.path.as_uri())
|
2016-03-21 09:22:22 +00:00
|
|
|
}
|
|
|
|
}
|
2016-08-26 08:55:11 +00:00
|
|
|
|
|
|
|
impl<'r> Collider<Request<'r>> for Route {
|
|
|
|
fn collides_with(&self, req: &Request) -> bool {
|
|
|
|
self.method == req.method
|
2016-10-05 02:26:33 +00:00
|
|
|
&& req.uri().collides_with(&self.path.as_uri())
|
2016-09-08 07:02:17 +00:00
|
|
|
&& req.accepts().collides_with(&self.content_type)
|
2016-08-26 08:55:11 +00:00
|
|
|
}
|
|
|
|
}
|