composable-tower-http 0.3.1

Highly customizable http utilities built on top of tower.
Documentation
use std::{ops::Deref, sync::Arc};

use crate::extract::extractor::Extractor;

use super::chainer::Chainer;

#[derive(Debug)]
pub struct ChainExtractorInner<Ex, C> {
    extractor: Ex,
    chainer: C,
}

impl<Ex, C> ChainExtractorInner<Ex, C> {
    pub const fn new(extractor: Ex, chainer: C) -> Self {
        Self { extractor, chainer }
    }
}

#[derive(Debug)]
pub struct ChainExtractor<Ex, C> {
    inner: Arc<ChainExtractorInner<Ex, C>>,
}

impl<Ex, C> ChainExtractor<Ex, C> {
    pub fn new(extractor: Ex, chainer: C) -> Self {
        Self {
            inner: Arc::new(ChainExtractorInner::new(extractor, chainer)),
        }
    }
}

impl<Ex, C> Clone for ChainExtractor<Ex, C> {
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
        }
    }
}

impl<Ex, C> Deref for ChainExtractor<Ex, C> {
    type Target = ChainExtractorInner<Ex, C>;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl<Ex, C> Extractor for ChainExtractor<Ex, C>
where
    Ex: Extractor + Send + Sync,
    C: Chainer<Ex::Extracted> + Send + Sync,
{
    type Extracted = C::Chained;

    type Error = ChainError<Ex::Error, C::Error>;

    async fn extract(&self, headers: &http::HeaderMap) -> Result<Self::Extracted, Self::Error> {
        let extracted = self
            .extractor
            .extract(headers)
            .await
            .map_err(ChainError::Extract)?;

        let chained = self
            .chainer
            .chain(extracted)
            .await
            .map_err(ChainError::Chain)?;

        Ok(chained)
    }
}

#[derive(Debug, thiserror::Error)]
pub enum ChainError<Ex, E> {
    #[error("Extraction error: {0}")]
    Extract(#[source] Ex),
    #[error("Chain error: {0}")]
    Chain(#[source] E),
}

#[cfg(feature = "axum")]
mod axum {
    use axum::response::{IntoResponse, Response};

    use super::ChainError;

    impl<Ex, E> IntoResponse for ChainError<Ex, E>
    where
        Ex: IntoResponse,
        E: IntoResponse,
    {
        fn into_response(self) -> Response {
            match self {
                ChainError::Extract(err) => err.into_response(),
                ChainError::Chain(err) => err.into_response(),
            }
        }
    }

    impl<Ex, E> From<ChainError<Ex, E>> for Response
    where
        Ex: IntoResponse,
        E: IntoResponse,
    {
        fn from(value: ChainError<Ex, E>) -> Self {
            value.into_response()
        }
    }
}