use std::mem;
use std::rc::Rc;
use std::task::{Context, Poll};
use futures::future::{ok, ready, LocalBoxFuture, Ready};
use crate::http::Method;
use crate::{Service, ServiceFactory};
use super::error::ErrorRenderer;
use super::error_default::DefaultError;
use super::extract::FromRequest;
use super::guard::{self, Guard};
use super::handler::{Handler, HandlerFn, HandlerWrapper};
use super::request::WebRequest;
use super::responder::Responder;
use super::response::WebResponse;
use super::HttpResponse;
pub struct Route<Err: ErrorRenderer = DefaultError> {
handler: Box<dyn HandlerFn<Err>>,
methods: Vec<Method>,
guards: Rc<Vec<Box<dyn Guard>>>,
}
impl<Err: ErrorRenderer> Route<Err> {
pub fn new() -> Route<Err> {
Route {
handler: Box::new(HandlerWrapper::new(|| ready(HttpResponse::NotFound()))),
methods: Vec::new(),
guards: Rc::new(Vec::new()),
}
}
pub(super) fn take_guards(&mut self) -> Vec<Box<dyn Guard>> {
for m in &self.methods {
Rc::get_mut(&mut self.guards)
.unwrap()
.push(Box::new(guard::Method(m.clone())));
}
mem::replace(Rc::get_mut(&mut self.guards).unwrap(), Vec::new())
}
pub(super) fn service(&self) -> RouteService<Err> {
RouteService {
handler: self.handler.clone_handler(),
guards: self.guards.clone(),
methods: self.methods.clone(),
}
}
}
impl<Err: ErrorRenderer> ServiceFactory for Route<Err> {
type Config = ();
type Request = WebRequest<Err>;
type Response = WebResponse;
type Error = Err::Container;
type InitError = ();
type Service = RouteService<Err>;
type Future = Ready<Result<RouteService<Err>, ()>>;
fn new_service(&self, _: ()) -> Self::Future {
ok(self.service())
}
}
pub struct RouteService<Err: ErrorRenderer> {
handler: Box<dyn HandlerFn<Err>>,
methods: Vec<Method>,
guards: Rc<Vec<Box<dyn Guard>>>,
}
impl<Err: ErrorRenderer> RouteService<Err> {
pub fn check(&self, req: &mut WebRequest<Err>) -> bool {
if !self.methods.is_empty() && !self.methods.contains(&req.head().method) {
return false;
}
for f in self.guards.iter() {
if !f.check(req.head()) {
return false;
}
}
true
}
}
impl<Err: ErrorRenderer> Service for RouteService<Err> {
type Request = WebRequest<Err>;
type Response = WebResponse;
type Error = Err::Container;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
#[inline]
fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
#[inline]
fn call(&self, req: WebRequest<Err>) -> Self::Future {
self.handler.call(req)
}
}
impl<Err: ErrorRenderer> Route<Err> {
pub fn method(mut self, method: Method) -> Self {
self.methods.push(method);
self
}
pub fn guard<F: Guard + 'static>(mut self, f: F) -> Self {
Rc::get_mut(&mut self.guards).unwrap().push(Box::new(f));
self
}
pub fn to<F, Args>(mut self, handler: F) -> Self
where
F: Handler<Args, Err>,
Args: FromRequest<Err> + 'static,
Args::Error: Into<Err::Container>,
<F::Output as Responder<Err>>::Error: Into<Err::Container>,
{
self.handler = Box::new(HandlerWrapper::new(handler));
self
}
}
pub trait IntoRoutes<Err: ErrorRenderer> {
fn routes(self) -> Vec<Route<Err>>;
}
impl<Err: ErrorRenderer> IntoRoutes<Err> for Route<Err> {
fn routes(self) -> Vec<Route<Err>> {
vec![self]
}
}
impl<Err: ErrorRenderer> IntoRoutes<Err> for Vec<Route<Err>> {
fn routes(self) -> Vec<Route<Err>> {
self
}
}
macro_rules! tuple_routes({$(($n:tt, $T:ty)),+} => {
#[allow(unused_parens)]
impl<Err: ErrorRenderer> IntoRoutes<Err> for ($($T,)+) {
fn routes(self) -> Vec<Route<Err>> {
vec![$(self.$n,)+]
}
}
});
macro_rules! array_routes({$num:tt, $($T:ident),+} => {
#[allow(unused_parens)]
impl<Err: ErrorRenderer> IntoRoutes<Err> for [Route<Err>; $num]
where
Err: ErrorRenderer,
{
fn routes(self) -> Vec<Route<Err>> {
let [$($T,)+] = self;
vec![$($T,)+]
}
}
});
#[rustfmt::skip]
mod m {
use super::*;
tuple_routes!((0,Route<Err>));
tuple_routes!((0,Route<Err>),(1,Route<Err>));
tuple_routes!((0,Route<Err>),(1,Route<Err>),(2,Route<Err>));
tuple_routes!((0,Route<Err>),(1,Route<Err>),(2,Route<Err>),(3,Route<Err>));
tuple_routes!((0,Route<Err>),(1,Route<Err>),(2,Route<Err>),(3,Route<Err>),(4,Route<Err>));
tuple_routes!((0,Route<Err>),(1,Route<Err>),(2,Route<Err>),(3,Route<Err>),(4,Route<Err>),(5,Route<Err>));
tuple_routes!((0,Route<Err>),(1,Route<Err>),(2,Route<Err>),(3,Route<Err>),(4,Route<Err>),(5,Route<Err>),(6,Route<Err>));
tuple_routes!((0,Route<Err>),(1,Route<Err>),(2,Route<Err>),(3,Route<Err>),(4,Route<Err>),(5,Route<Err>),(6,Route<Err>),(7,Route<Err>));
tuple_routes!((0,Route<Err>),(1,Route<Err>),(2,Route<Err>),(3,Route<Err>),(4,Route<Err>),(5,Route<Err>),(6,Route<Err>),(7,Route<Err>),(8,Route<Err>));
tuple_routes!((0,Route<Err>),(1,Route<Err>),(2,Route<Err>),(3,Route<Err>),(4,Route<Err>),(5,Route<Err>),(6,Route<Err>),(7,Route<Err>),(8,Route<Err>),(9,Route<Err>));
tuple_routes!((0,Route<Err>),(1,Route<Err>),(2,Route<Err>),(3,Route<Err>),(4,Route<Err>),(5,Route<Err>),(6,Route<Err>),(7,Route<Err>),(8,Route<Err>),(9,Route<Err>),(10,Route<Err>));
tuple_routes!((0,Route<Err>),(1,Route<Err>),(2,Route<Err>),(3,Route<Err>),(4,Route<Err>),(5,Route<Err>),(6,Route<Err>),(7,Route<Err>),(8,Route<Err>),(9,Route<Err>),(10,Route<Err>),(11,Route<Err>));
}
array_routes!(1, a);
array_routes!(2, a, b);
array_routes!(3, a, b, c);
array_routes!(4, a, b, c, d);
array_routes!(5, a, b, c, d, e);
array_routes!(6, a, b, c, d, e, f);
array_routes!(7, a, b, c, d, e, f, g);
array_routes!(8, a, b, c, d, e, f, g, h);
array_routes!(9, a, b, c, d, e, f, g, h, i);
array_routes!(10, a, b, c, d, e, f, g, h, i, j);
array_routes!(11, a, b, c, d, e, f, g, h, i, j, k);
array_routes!(12, a, b, c, d, e, f, g, h, i, j, k, l);
#[cfg(test)]
mod tests {
use std::time::Duration;
use bytes::Bytes;
use serde_derive::Serialize;
use crate::http::{Method, StatusCode};
use crate::rt::time::delay_for;
use crate::web::test::{call_service, init_service, read_body, TestRequest};
use crate::web::{self, error, App, DefaultError, HttpResponse};
#[derive(Serialize, PartialEq, Debug)]
struct MyObject {
name: String,
}
#[ntex_rt::test]
async fn test_route() {
let srv = init_service(
App::new()
.service(web::resource("/test").route(vec![
web::get().to(|| async { HttpResponse::Ok() }),
web::put().to(|| async {
Err::<HttpResponse, _>(
error::ErrorBadRequest::<_, DefaultError>("err"),
)
}),
web::post().to(|| async {
delay_for(Duration::from_millis(100)).await;
HttpResponse::Created()
}),
web::delete().to(|| async {
delay_for(Duration::from_millis(100)).await;
Err::<HttpResponse, _>(error::ErrorBadRequest("err"))
}),
]))
.service(web::resource("/json").route(web::get().to(|| async {
delay_for(Duration::from_millis(25)).await;
web::types::Json(MyObject {
name: "test".to_string(),
})
}))),
)
.await;
let req = TestRequest::with_uri("/test")
.method(Method::GET)
.to_request();
let resp = call_service(&srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let req = TestRequest::with_uri("/test")
.method(Method::POST)
.to_request();
let resp = call_service(&srv, req).await;
assert_eq!(resp.status(), StatusCode::CREATED);
let req = TestRequest::with_uri("/test")
.method(Method::PUT)
.to_request();
let resp = call_service(&srv, req).await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/test")
.method(Method::DELETE)
.to_request();
let resp = call_service(&srv, req).await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/test")
.method(Method::HEAD)
.to_request();
let resp = call_service(&srv, req).await;
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let req = TestRequest::with_uri("/json").to_request();
let resp = call_service(&srv, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let body = read_body(resp).await;
assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}"));
}
}