use std::{io, fmt};
use std::ops::{Deref, DerefMut};
use hyper::server::{Service, NewService};
use hyper::{Request, Response, StatusCode};
use futures::{future, Future};
pub trait GetPath {
fn path(&self) -> &str;
}
impl GetPath for Request {
fn path(&self) -> &str {
self.path()
}
}
impl<C> GetPath for (Request, C) {
fn path(&self) -> &str {
self.0.path()
}
}
pub trait NotFound {
fn not_found() -> Self;
}
impl NotFound for Response {
fn not_found() -> Self {
Response::new().with_status(StatusCode::NotFound)
}
}
type BoxedFuture<V, W> = Box<Future<Item = V, Error = W>>;
type CompositeNewServiceVec<U, V, W> = Vec<(&'static str, Box<BoxedNewService<U, V, W>>)>;
type BoxedService<U, V, W> = Box<
Service<
Request = U,
Response = V,
Error = W,
Future = BoxedFuture<V, W>,
>,
>;
pub trait BoxedNewService<U, V, W> {
fn boxed_new_service(&self) -> Result<BoxedService<U, V, W>, io::Error>;
}
impl<T, U, V, W> BoxedNewService<U, V, W> for T
where
T: NewService<Request = U, Response = V, Error = W>,
T::Instance: Service<Future = BoxedFuture<V, W>>
+ 'static,
{
fn boxed_new_service(
&self,
) -> Result<
BoxedService<U, V, W>,
io::Error,
> {
let service = self.new_service()?;
Ok(Box::new(service))
}
}
#[derive(Default)]
pub struct CompositeNewService<U, V, W>(CompositeNewServiceVec<U, V, W>)
where
U: GetPath,
V: NotFound + 'static,
W: 'static;
#[cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))]
impl<U: GetPath, V: NotFound, W> CompositeNewService<U, V, W> {
pub fn new() -> Self {
CompositeNewService(Vec::new())
}
}
impl<U, V, W> NewService for CompositeNewService<U, V, W>
where
U: GetPath,
V: NotFound + 'static,
W: 'static,
{
type Request = U;
type Response = V;
type Error = W;
type Instance = CompositeService<U, V, W>;
fn new_service(&self) -> Result<Self::Instance, io::Error> {
let mut vec = Vec::new();
for &(base_path, ref new_service) in &self.0 {
vec.push((base_path, new_service.boxed_new_service()?))
}
Ok(CompositeService(vec))
}
}
impl<U, V, W> fmt::Debug for CompositeNewService<U, V, W>
where
U: GetPath,
V: NotFound + 'static,
W: 'static,
{
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let str_vec: Vec<&'static str> = self.0.iter().map(|&(base_path, _)| base_path).collect();
write!(
f,
"CompositeNewService accepting base paths: {:?}",
str_vec,
)
}
}
impl<U, V, W> Deref for CompositeNewService<U, V, W>
where
U: GetPath,
V: NotFound + 'static,
W: 'static,
{
type Target = CompositeNewServiceVec<U, V, W>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<U, V, W> DerefMut for CompositeNewService<U, V, W>
where
U: GetPath,
V: NotFound + 'static,
W: 'static,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub struct CompositeService<U, V, W>(Vec<(&'static str, BoxedService<U, V, W>)>)
where
U: GetPath,
V: NotFound + 'static,
W: 'static;
impl<U, V, W> Service for CompositeService<U, V, W>
where
U: GetPath,
V: NotFound + 'static,
W: 'static,
{
type Request = U;
type Response = V;
type Error = W;
type Future = Box<Future<Item = V, Error = W>>;
fn call(&self, req: Self::Request) -> Self::Future {
let mut result = None;
for &(base_path, ref service) in &self.0 {
if req.path().starts_with(base_path) {
result = Some(service.call(req));
break;
}
}
result.unwrap_or_else(|| Box::new(future::ok(V::not_found())))
}
}
impl<U, V, W> fmt::Debug for CompositeService<U, V, W>
where
U: GetPath,
V: NotFound + 'static,
W: 'static,
{
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let str_vec: Vec<&'static str> = self.0.iter().map(|&(base_path, _)| base_path).collect();
write!(
f,
"CompositeService accepting base paths: {:?}",
str_vec,
)
}
}
impl<U, V, W> Deref for CompositeService<U, V, W>
where
U: GetPath,
V: NotFound + 'static,
W: 'static,
{
type Target = Vec<(&'static str, BoxedService<U, V, W>)>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<U, V, W> DerefMut for CompositeService<U, V, W>
where
U: GetPath,
V: NotFound + 'static,
W: 'static,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}