use crate::{
body::Body,
guard::{Builder as GuardBuilder, GuardChain, GuardChainEnd},
request::Request,
responder::{DynResponder, Responder},
};
use futures::future::BoxFuture;
use futures_util::future::{Future, FutureExt};
use http::Method;
pub type ControllerEndpoint<C> = (
Option<&'static str>,
Method,
&'static str,
Box<dyn DynControllerHandler<C, Body> + Send + Sync>,
Box<dyn GuardChain>,
);
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: &'static C, req: Request<B>) -> Self::Future;
}
pub trait DynControllerHandler<C, B> {
fn dyn_handle(&self, controller: &'static C, req: Request<B>) -> BoxFuture<'static, Box<dyn DynResponder + Send>>;
}
#[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((None, method, route, Box::new(handler), GuardBuilder::default().build()));
self
}
#[inline]
pub fn add_with_guards<H, F, Chain>(mut self, method: Method, route: &'static str, handler: H, guards: F) -> Self
where
H: 'static + DynControllerHandler<C, Body> + Send + Sync,
F: FnOnce(GuardBuilder<GuardChainEnd>) -> GuardBuilder<Chain>,
Chain: GuardChain + 'static,
{
self.handlers
.push((None, method, route, Box::new(handler), guards(GuardBuilder::default()).build()));
self
}
#[inline]
pub fn add_with_name<H>(mut self, handler_name: &'static str, method: Method, route: &'static str, handler: H) -> Self
where
H: 'static + DynControllerHandler<C, Body> + Send + Sync,
{
self.handlers
.push((Some(handler_name), method, route, Box::new(handler), GuardBuilder::default().build()));
self
}
#[inline]
pub fn add_with_guards_and_name<H, F, Chain>(mut self, handler_name: &'static str, method: Method, route: &'static str, handler: H, guards: F) -> Self
where
H: 'static + DynControllerHandler<C, Body> + Send + Sync,
F: FnOnce(GuardBuilder<GuardChainEnd>) -> GuardBuilder<Chain>,
Chain: GuardChain + 'static,
{
self.handlers
.push((Some(handler_name), method, route, Box::new(handler), guards(GuardBuilder::default()).build()));
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 Future = Box<dyn Future<Output = Self::Responder> + Unpin + Send>;
type Responder = R;
#[inline]
fn handle(&self, controller: &'static C, req: Request<B>) -> Self::Future {
Box::new(Box::pin((*self)(controller, req)))
}
}
impl<C, T, H, Fut, R> DynControllerHandler<C, T> for H
where
R: 'static + Responder + Send,
Fut: 'static + Future<Output = R> + Unpin + Send,
H: ControllerHandler<C, T, Future = Fut, Responder = R>,
{
#[inline]
fn dyn_handle(&self, controller: &'static C, req: Request<T>) -> BoxFuture<'static, Box<dyn DynResponder + Send>> {
self.handle(controller, req).map(|r| Box::new(Some(r)) as Box<dyn DynResponder + Send>).boxed()
}
}