use std::{
collections::HashMap,
convert::Infallible,
sync::Arc,
task::{Context, Poll},
};
use dyn_clone::clone_box;
use http::{Method, Request, Response, StatusCode};
use hyper::Body;
use tower::{Service, ServiceExt};
use crate::service::{BoxHttpResponseFuture, HttpService};
pub struct RouteByMethod {
method_services: Arc<HashMap<Method, Box<dyn HttpService<Body, Body>>>>,
poll_ready: bool,
}
impl std::fmt::Debug for RouteByMethod {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RouteByMethod")
.field("poll_ready", &self.poll_ready)
.finish()
}
}
impl Clone for RouteByMethod {
#[inline]
fn clone(&self) -> Self {
Self {
method_services: self.method_services.clone(),
poll_ready: self.poll_ready,
}
}
}
impl RouteByMethod {
#[inline]
pub fn new(method_services: Arc<HashMap<Method, Box<dyn HttpService<Body, Body>>>>) -> Self {
Self {
method_services,
poll_ready: false,
}
}
}
impl Service<Request<Body>> for RouteByMethod {
type Response = Response<Body>;
type Error = Infallible;
type Future = BoxHttpResponseFuture<Body>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
if self.poll_ready {
return Poll::Ready(Ok(()));
}
for s in self.method_services.values() {
if let Poll::Ready(result) = clone_box(s.as_ref()).poll_ready(cx) {
assert!(result.is_ok(), "Method services must be infallible");
} else {
return Poll::Pending;
}
}
self.poll_ready = true;
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
if let Some(method_service) = self.method_services.get(req.method()) {
let mut method_service = clone_box(method_service.as_ref());
Box::pin(async move {
let response: Response<Body> = method_service
.ready()
.await
.expect("Must be infallible")
.call(req)
.await
.expect("Method service must be infallible.");
Ok(response)
})
} else {
Box::pin(futures::future::ready(Ok(Response::builder()
.status(StatusCode::NOT_IMPLEMENTED)
.body(Body::empty())
.expect("Must be valid."))))
}
}
}