use crate::{
request::Request,
responder::{DynResponder, Responder},
};
use futures::future::BoxFuture;
use futures_util::future::{Future, FutureExt};
use http::Method;
use hyper::Body;
pub type ControllerEndpoint<C> = (
Method,
&'static str,
Box<dyn DynControllerHandler<C, Body> + Send + Sync>,
);
pub trait Controller {
const BASE_PATH: &'static str;
fn handlers(&self) -> Vec<ControllerEndpoint<Self>>
where
Self: Sized;
}
pub trait ControllerHandler<C, B> {
type Responder: Responder;
type Future: Future<Output=Self::Responder>;
fn handle(&self, controller: &C, req: Request<B>) -> Self::Future;
}
pub trait DynControllerHandler<C, B> {
fn dyn_handle(&self, controller: &C, req: Request<B>) -> BoxFuture<'static, Box<dyn DynResponder>>;
}
#[derive(Default)]
pub struct EndpointsBuilder<C: Controller> {
handlers: Vec<ControllerEndpoint<C>>,
}
impl<C: Controller> EndpointsBuilder<C> {
#[inline]
pub fn new() -> Self {
Self {
handlers: Default::default(),
}
}
#[inline]
pub fn add<H>(mut self, method: Method, route: &'static str, handler: H) -> Self
where
H: 'static + DynControllerHandler<C, Body> + Send + Sync,
{
self.handlers.push((method, route, Box::new(handler)));
self
}
#[inline]
pub fn build(self) -> Vec<ControllerEndpoint<C>> {
self.handlers
}
}
impl<C, B, Fun, Fut, R> ControllerHandler<C, B> for Fun
where
C: 'static,
Fun: Fn(&'static C, Request<B>) -> Fut,
Fut: 'static + Future<Output=R> + Send,
R: Responder,
{
type Responder = R;
type Future = Box<dyn Future<Output=Self::Responder> + Unpin + Send>;
#[inline]
fn handle(&self, controller: &C, req: Request<B>) -> Self::Future {
let controller = unsafe { std::mem::transmute::<&'_ C, &'static C>(controller) };
Box::new(Box::pin((*self)(controller, req)))
}
}
impl<C, T, H, Fut, R> DynControllerHandler<C, T> for H
where
R: 'static + Responder,
Fut: 'static + Future<Output=R> + Unpin + Send,
H: ControllerHandler<C, T, Future=Fut, Responder=R>,
{
#[inline]
fn dyn_handle(&self, controller: &C, req: Request<T>) -> BoxFuture<'static, Box<dyn DynResponder>> {
self.handle(controller, req)
.map(|r| Box::new(Some(r)) as Box<dyn DynResponder>)
.boxed()
}
}