use hyper::Method;
use hyper::{Body, Request, Response, StatusCode};
use regex::{Regex, RegexSet};
use smallvec::SmallVec;
pub use error::Error;
mod error;
pub type Captures<'r> = Option<SmallVec<[&'r str; 4]>>;
type RouteHandler = Box<dyn Fn(Request<Body>, Captures) -> Response<Body> + Send + Sync>;
pub struct Router {
routes: RegexSet,
patterns: Vec<Regex>,
handlers: Vec<(Method, RouteHandler)>,
not_found: RouteHandler,
}
impl Router {
pub fn handle(&self, req: Request<Body>) -> Response<Body> {
let uri = req.uri().clone();
let uri = uri.path();
let matches = self.routes.matches(uri);
if !matches.matched_any() {
return (self.not_found)(req, None);
}
for index in matches {
let (ref method, ref handler) = self.handlers[index];
if method != req.method() {
continue;
}
let regex = &self.patterns[index];
let captures = get_captures(regex, uri);
return handler(req, captures);
}
not_allowed()
}
}
#[derive(Default)]
pub struct RouterBuilder {
routes: Vec<String>,
handlers: Vec<(Method, RouteHandler)>,
not_found: Option<RouteHandler>,
}
impl RouterBuilder {
pub fn new() -> RouterBuilder {
RouterBuilder::default()
}
pub fn route<H>(&mut self, verb: Method, route: &str, handler: H) -> &mut RouterBuilder
where
H: Fn(Request<Body>, Captures) -> Response<Body> + Send + Sync + 'static,
{
let pattern = [r"\A", route, r"\z"].join("");
self.routes.push(pattern);
self.handlers.push((verb, Box::new(handler)));
self
}
pub fn finalize(self) -> Result<Router, Error> {
Ok(Router {
routes: RegexSet::new(self.routes.iter())?,
patterns: self
.routes
.iter()
.map(|route| Regex::new(route))
.collect::<Result<_, _>>()?,
handlers: self.handlers,
not_found: self
.not_found
.unwrap_or_else(|| Box::new(default_not_found)),
})
}
pub fn get<H>(&mut self, route: &str, handler: H) -> &mut RouterBuilder
where
H: Fn(Request<Body>, Captures) -> Response<Body> + Send + Sync + 'static,
{
self.route(Method::GET, route, handler)
}
pub fn post<H>(&mut self, route: &str, handler: H) -> &mut RouterBuilder
where
H: Fn(Request<Body>, Captures) -> Response<Body> + Send + Sync + 'static,
{
self.route(Method::POST, route, handler)
}
pub fn put<H>(&mut self, route: &str, handler: H) -> &mut RouterBuilder
where
H: Fn(Request<Body>, Captures) -> Response<Body> + Send + Sync + 'static,
{
self.route(Method::PUT, route, handler)
}
pub fn patch<H>(&mut self, route: &str, handler: H) -> &mut RouterBuilder
where
H: Fn(Request<Body>, Captures) -> Response<Body> + Send + Sync + 'static,
{
self.route(Method::PATCH, route, handler)
}
pub fn delete<H>(&mut self, route: &str, handler: H) -> &mut RouterBuilder
where
H: Fn(Request<Body>, Captures) -> Response<Body> + Send + Sync + 'static,
{
self.route(Method::DELETE, route, handler)
}
pub fn options<H>(&mut self, route: &str, handler: H) -> &mut RouterBuilder
where
H: Fn(Request<Body>, Captures) -> Response<Body> + Send + Sync + 'static,
{
self.route(Method::OPTIONS, route, handler)
}
pub fn not_found<H>(&mut self, not_found: H) -> &mut RouterBuilder
where
H: Fn(Request<Body>, Captures) -> Response<Body> + Send + Sync + 'static,
{
self.not_found = Some(Box::new(not_found));
self
}
}
fn default_not_found(_: Request<Body>, _: Captures) -> Response<Body> {
Response::builder()
.status(StatusCode::NOT_FOUND)
.body("Not Found".into())
.unwrap()
}
fn not_allowed() -> Response<Body> {
Response::builder()
.status(StatusCode::METHOD_NOT_ALLOWED)
.body("Method Not Allowed".into())
.unwrap()
}
fn get_captures<'r>(pattern: &'r Regex, uri: &'r str) -> Captures<'r> {
let caps = pattern.captures(uri);
match caps {
Some(caps) => {
let mut v = SmallVec::<[&str; 4]>::new();
caps.iter()
.filter(|c| c.is_some())
.for_each(|c| v.push(c.unwrap().as_str()));
Some(v)
}
None => None,
}
}
#[test]
fn bad_regular_expression() {
fn test_handler(_: Request<Body>, _: Captures) -> Response<Body> {
Response::builder()
.status(StatusCode::OK)
.body("Ok".into())
.unwrap()
}
let mut router = RouterBuilder::new();
router.route(Method::GET, r"/[", test_handler);
let e = router.finalize();
assert!(e.is_err());
}