tower_type_steer 0.1.0

Steer (aka route, aka dispatch) request to service based on their types.
Documentation
use std::task::{Context, Poll};

use futures_util::future::Either;
use tower_layer::Layer;
use tower_service::Service;

#[cfg(feature = "serde")]
pub mod serde_conv;

pub trait Converter<Fro> {
    type To;

    fn try_convert(&mut self, from: Fro) -> Result<Self::To, Fro>;

    fn wrap<S>(self, srvc: S) -> TryConvert<Self, S>
    where
        Self: Sized,
    {
        TryConvert {
            converter: self,
            inner: srvc,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct TryConvert<Conv, S> {
    converter: Conv,
    inner: S,
}

impl<Conv: Clone, S: Clone, SOr> Layer<SOr> for TryConvert<Conv, S> {
    type Service = TryConvertOr<Conv, S, SOr>;

    fn layer(&self, inner: SOr) -> Self::Service {
        TryConvertOr::new(self.clone(), inner)
    }
}

pub struct TryConvertOr<Conv, S1, S2> {
    try_convert: TryConvert<Conv, S1>,
    or: S2,
    s1_ready: bool,
    s2_ready: bool,
}

impl<Conv, S1, S2> TryConvertOr<Conv, S1, S2> {
    pub fn new(try_convert: TryConvert<Conv, S1>, or: S2) -> Self {
        Self {
            try_convert,
            or,
            s1_ready: false,
            s2_ready: false,
        }
    }
}

impl<RBefore, Conv, S1, S2> Service<RBefore> for TryConvertOr<Conv, S1, S2>
where
    Conv: Converter<RBefore>,
    S1: Service<Conv::To>,
    S2: Service<RBefore, Response = S1::Response, Error = S1::Error>,
{
    type Response = S1::Response;

    type Error = S1::Error;

    type Future = Either<S1::Future, S2::Future>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        if !self.s1_ready {
            if self.try_convert.inner.poll_ready(cx)?.is_pending() {
                return Poll::Pending;
            }
            self.s1_ready = true;
        }
        if !self.s2_ready {
            if self.or.poll_ready(cx)?.is_pending() {
                return Poll::Pending;
            }
            self.s2_ready = true;
        }
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, req: RBefore) -> Self::Future {
        match self.try_convert.converter.try_convert(req) {
            Ok(s1_req) => Either::Left(self.try_convert.inner.call(s1_req)),
            Err(req) => Either::Right(self.or.call(req)),
        }
    }
}