use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use bytes::Bytes;
use http_body_util::Full;
use hyper::body::Incoming;
use tower::util::BoxCloneService;
use tower_layer::Layer;
use tower_service::Service;
use oxihttp_core::OxiHttpError;
use crate::router::Router;
pub type BoxedRouterService =
BoxCloneService<http::Request<Incoming>, http::Response<Full<Bytes>>, OxiHttpError>;
#[derive(Clone)]
pub struct RouterService {
router: Arc<Router>,
}
impl RouterService {
pub fn new(router: Arc<Router>) -> Self {
Self { router }
}
}
impl Service<http::Request<Incoming>> for RouterService {
type Response = http::Response<Full<Bytes>>;
type Error = OxiHttpError;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: http::Request<Incoming>) -> Self::Future {
let router = Arc::clone(&self.router);
Box::pin(async move { router.dispatch(req).await })
}
}
pub trait ErasedLayer: Send + Sync {
fn layer_boxed(&self, svc: BoxedRouterService) -> BoxedRouterService;
}
pub struct OwnedLayer<L>(pub L);
impl<L> ErasedLayer for OwnedLayer<L>
where
L: Layer<BoxedRouterService> + Send + Sync + Clone + 'static,
L::Service: Service<
http::Request<Incoming>,
Response = http::Response<Full<Bytes>>,
Error = OxiHttpError,
> + Clone
+ Send
+ 'static,
<L::Service as Service<http::Request<Incoming>>>::Future: Send + 'static,
{
fn layer_boxed(&self, svc: BoxedRouterService) -> BoxedRouterService {
BoxCloneService::new(self.0.clone().layer(svc))
}
}
#[derive(Clone)]
pub struct RouterMakeService(pub Arc<Router>);
impl RouterMakeService {
pub fn make(&self) -> RouterService {
RouterService::new(Arc::clone(&self.0))
}
}
pub fn build_layered_service(
router: Arc<Router>,
layers: &[Arc<dyn ErasedLayer>],
) -> BoxedRouterService {
let base: BoxedRouterService = BoxCloneService::new(RouterService::new(router));
layers
.iter()
.rev()
.fold(base, |svc, layer| layer.layer_boxed(svc))
}