#![doc(html_root_url = "https://tsharp.github.io/hyper-routing/doc/hyper_routing")]
extern crate futures;
extern crate hyper;
use std::task::{Context, Poll};
use futures::future;
use hyper::header::CONTENT_LENGTH;
use hyper::service::Service;
use hyper::{Body, Request, Response};
use hyper::Method;
use hyper::StatusCode;
mod builder;
pub mod handlers;
mod path;
pub mod route;
pub use self::builder::RouterBuilder;
pub use self::path::Path;
pub use self::route::Route;
pub use self::route::RouteBuilder;
pub type Handler = fn(Request<Body>) -> Response<Body>;
pub type HttpResult<T> = Result<T, StatusCode>;
#[derive(Debug)]
pub struct Router {
routes: Vec<Route>,
}
impl Router {
pub fn find_handler_with_defaults(&self, request: &Request<Body>) -> Handler {
let matching_routes = self.find_matching_routes(request.uri().path());
match matching_routes.len() {
x if x == 0 => handlers::default_404_handler,
_ => self
.find_for_method(&matching_routes, request.method())
.unwrap_or(handlers::method_not_supported_handler),
}
}
pub fn find_handler(&self, request: &Request<Body>) -> HttpResult<Handler> {
let matching_routes = self.find_matching_routes(request.uri().path());
match matching_routes.len() {
x if x == 0 => Err(StatusCode::NOT_FOUND),
_ => self
.find_for_method(&matching_routes, request.method())
.map(Ok)
.unwrap_or(Err(StatusCode::METHOD_NOT_ALLOWED)),
}
}
pub fn find_matching_routes(&self, request_path: &str) -> Vec<&Route> {
self.routes
.iter()
.filter(|route| route.path.matcher.is_match(&request_path))
.collect()
}
fn find_for_method(&self, routes: &[&Route], method: &Method) -> Option<Handler> {
let method = method.clone();
routes
.iter()
.find(|route| route.method == method)
.map(|route| route.handler)
}
}
#[derive(Debug)]
pub struct RouterService {
pub router: Router,
pub error_handler: fn(StatusCode) -> Response<Body>,
}
impl RouterService {
pub fn new(router: Router) -> RouterService {
RouterService {
router,
error_handler: Self::default_error_handler,
}
}
fn default_error_handler(status_code: StatusCode) -> Response<Body> {
let error = "Routing error: page not found";
Response::builder()
.header(CONTENT_LENGTH, error.len() as u64)
.status(match status_code {
StatusCode::NOT_FOUND => StatusCode::NOT_FOUND,
_ => StatusCode::INTERNAL_SERVER_ERROR,
})
.body(Body::from(error))
.expect("Failed to construct a response")
}
}
impl Service<Request<Body>> for RouterService {
type Response = Response<Body>;
type Error = hyper::Error;
type Future = future::Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Ok(()).into()
}
fn call(&mut self, request: Request<Body>) -> Self::Future {
futures::future::ok(match self.router.find_handler(&request) {
Ok(handler) => handler(request),
Err(status_code) => (self.error_handler)(status_code),
})
}
}