hyperdriver/service/
option.rsuse tower::layer::util::Stack;
use tower::ServiceBuilder;
pub trait OptionLayerExt<S> {
fn optional<T>(self, middleware: Option<T>) -> ServiceBuilder<Stack<OptionLayer<T>, S>>;
}
impl<S> OptionLayerExt<S> for ServiceBuilder<S> {
fn optional<T>(self, middleware: Option<T>) -> ServiceBuilder<Stack<OptionLayer<T>, S>> {
self.layer(OptionLayer::new(middleware))
}
}
#[derive(Debug, Clone)]
pub struct OptionLayer<M> {
middleware: Option<M>,
}
impl<M> OptionLayer<M> {
pub fn new(middleware: Option<M>) -> Self {
Self { middleware }
}
}
impl<M, S> tower::Layer<S> for OptionLayer<M>
where
M: tower::Layer<S>,
{
type Service = OptionService<M::Service, S>;
fn layer(&self, inner: S) -> Self::Service {
match self.middleware {
Some(ref middleware) => OptionService {
inner: OptionServiceState::Middleware(middleware.layer(inner)),
},
None => OptionService {
inner: OptionServiceState::Service(inner),
},
}
}
}
#[derive(Debug, Clone)]
enum OptionServiceState<M, S> {
Middleware(M),
Service(S),
}
#[derive(Clone, Debug)]
pub struct OptionService<M, S> {
inner: OptionServiceState<M, S>,
}
impl<M, S, Req, E> tower::Service<Req> for OptionService<M, S>
where
S: tower::Service<Req, Error = E>,
M: tower::Service<Req, Response = S::Response>,
M::Error: Into<E>,
{
type Response = S::Response;
type Error = E;
type Future = self::future::OptionServiceFuture<M::Future, S::Future>;
fn poll_ready(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
match self.inner {
OptionServiceState::Middleware(ref mut m) => m.poll_ready(cx).map_err(Into::into),
OptionServiceState::Service(ref mut s) => s.poll_ready(cx),
}
}
fn call(&mut self, req: Req) -> Self::Future {
match self.inner {
OptionServiceState::Middleware(ref mut m) => {
self::future::OptionServiceFuture::middleware(m.call(req))
}
OptionServiceState::Service(ref mut s) => {
self::future::OptionServiceFuture::service(s.call(req))
}
}
}
}
mod future {
use std::future::Future;
use pin_project::pin_project;
#[derive(Debug)]
#[pin_project(project = StateProj)]
enum State<M, S> {
Middleware(#[pin] M),
Service(#[pin] S),
}
#[derive(Debug)]
#[pin_project]
pub struct OptionServiceFuture<M, S> {
#[pin]
state: State<M, S>,
}
impl<M, S> OptionServiceFuture<M, S> {
pub(super) fn service(inner: S) -> Self {
Self {
state: State::Service(inner),
}
}
pub(super) fn middleware(inner: M) -> Self {
Self {
state: State::Middleware(inner),
}
}
}
impl<M, S, R, E, ME> Future for OptionServiceFuture<M, S>
where
M: Future<Output = Result<R, ME>>,
S: Future<Output = Result<R, E>>,
ME: Into<E>,
{
type Output = Result<R, E>;
fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
match self.project().state.project() {
StateProj::Middleware(future) => {
future.poll(cx).map(|result| result.map_err(Into::into))
}
StateProj::Service(future) => future.poll(cx),
}
}
}
}