volga 0.8.9

Easy & Fast Web Framework for Rust
Documentation
use hyper::{Request, body::Incoming};
use std::sync::Arc;

use crate::{
    HttpResult,
    error::{
        FallbackFunc,
        fallback::{PipelineFallbackHandler, default_fallback_handler},
        handler::{DefaultErrorHandler, PipelineErrorHandler},
    },
    http::endpoints::Endpoints,
};

#[cfg(feature = "middleware")]
use crate::middleware::{HttpContext, Middlewares, NextFn};

pub(crate) struct PipelineBuilder {
    #[cfg(feature = "middleware")]
    middlewares: Middlewares,
    endpoints: Endpoints,
    error_handler: PipelineErrorHandler,
    fallback_handler: PipelineFallbackHandler,
}

impl std::fmt::Debug for PipelineBuilder {
    #[inline]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("PipelineBuilder(..)")
    }
}

pub(crate) struct Pipeline {
    #[cfg(feature = "middleware")]
    start: Option<NextFn>,
    endpoints: Endpoints,
    error_handler: PipelineErrorHandler,
    fallback_handler: PipelineFallbackHandler,
}

impl PipelineBuilder {
    #[cfg(feature = "middleware")]
    pub(super) fn new() -> Self {
        Self {
            middlewares: Middlewares::new(),
            endpoints: Endpoints::new(),
            error_handler: Arc::new(DefaultErrorHandler),
            fallback_handler: FallbackFunc::new(default_fallback_handler).into(),
        }
    }

    #[cfg(not(feature = "middleware"))]
    pub(super) fn new() -> Self {
        Self {
            endpoints: Endpoints::new(),
            error_handler: Arc::new(DefaultErrorHandler),
            fallback_handler: FallbackFunc::new(default_fallback_handler).into(),
        }
    }

    #[cfg(feature = "middleware")]
    pub(super) fn build(mut self) -> Pipeline {
        let start = self.middlewares.compose();
        self.endpoints.compose();
        Pipeline {
            endpoints: self.endpoints,
            error_handler: self.error_handler,
            fallback_handler: self.fallback_handler,
            start,
        }
    }

    #[cfg(not(feature = "middleware"))]
    pub(super) fn build(self) -> Pipeline {
        Pipeline {
            endpoints: self.endpoints,
            error_handler: self.error_handler,
            fallback_handler: self.fallback_handler,
        }
    }

    #[cfg(feature = "middleware")]
    pub(crate) fn has_middleware_pipeline(&self) -> bool {
        !self.middlewares.is_empty()
    }

    #[cfg(feature = "middleware")]
    pub(crate) fn middlewares_mut(&mut self) -> &mut Middlewares {
        &mut self.middlewares
    }

    pub(crate) fn endpoints_mut(&mut self) -> &mut Endpoints {
        &mut self.endpoints
    }

    pub(crate) fn endpoints(&self) -> &Endpoints {
        &self.endpoints
    }

    pub(crate) fn set_error_handler(&mut self, handler: PipelineErrorHandler) {
        self.error_handler = handler;
    }

    pub(crate) fn set_fallback_handler(&mut self, handler: PipelineFallbackHandler) {
        self.fallback_handler = handler;
    }
}

impl Pipeline {
    #[inline]
    pub(crate) fn endpoints(&self) -> &Endpoints {
        &self.endpoints
    }

    #[inline]
    pub(super) fn error_handler(&self) -> &PipelineErrorHandler {
        &self.error_handler
    }

    #[inline]
    pub(super) async fn fallback(&self, req: Request<Incoming>) -> HttpResult {
        self.fallback_handler.call(req).await
    }

    #[cfg(feature = "middleware")]
    pub(crate) fn has_middleware_pipeline(&self) -> bool {
        self.start.is_some()
    }

    #[cfg(feature = "middleware")]
    pub(crate) async fn execute(&self, ctx: HttpContext) -> HttpResult {
        if let Some(next) = &self.start {
            next(ctx).await
        } else {
            ctx.execute().await
        }
    }
}

#[cfg(test)]
mod tests {
    use super::PipelineBuilder;
    use crate::error::{
        ErrorFunc, FallbackFunc, fallback::PipelineFallbackHandler, handler::PipelineErrorHandler,
    };
    use crate::status;

    #[test]
    fn it_sets_error_and_fallback_handlers() {
        let mut builder = PipelineBuilder::new();

        let error_handler: PipelineErrorHandler =
            ErrorFunc::new(|_err| async { status!(418) }).into();
        builder.set_error_handler(error_handler.clone());
        assert!(std::sync::Arc::ptr_eq(
            &builder.error_handler,
            &error_handler
        ));

        let fallback_handler: PipelineFallbackHandler =
            FallbackFunc::new(|| async { status!(404) }).into();
        builder.set_fallback_handler(fallback_handler.clone());
        assert!(std::sync::Arc::ptr_eq(
            &builder.fallback_handler,
            &fallback_handler
        ));
    }

    #[cfg(feature = "middleware")]
    #[test]
    fn it_builds_without_middleware_pipeline() {
        let builder = PipelineBuilder::new();
        assert!(!builder.has_middleware_pipeline());

        let pipeline = builder.build();
        assert!(!pipeline.has_middleware_pipeline());
    }
}