Thanks to visit codestin.com
Credit goes to docs.rs

actix_web_lab/
condition_option.rs

1//! For middleware documentation, see [`ConditionOption`].
2
3use std::{
4    pin::Pin,
5    task::{self, Poll, ready},
6};
7
8use actix_web::{
9    body::EitherBody,
10    dev::{Service, ServiceResponse, Transform},
11};
12use futures_core::future::LocalBoxFuture;
13use futures_util::future::FutureExt as _;
14use pin_project_lite::pin_project;
15
16/// Middleware for conditionally enabling other middleware in an [`Option`].
17///
18/// # Example
19/// ```
20/// use actix_web::{App, middleware::Logger};
21/// use actix_web_lab::middleware::ConditionOption;
22///
23/// let normalize: ConditionOption<_> = Some(Logger::default()).into();
24/// let app = App::new().wrap(normalize);
25/// ```
26#[derive(Debug)]
27pub struct ConditionOption<T> {
28    inner: Option<T>,
29}
30
31impl<T> From<Option<T>> for ConditionOption<T> {
32    fn from(value: Option<T>) -> Self {
33        Self { inner: value }
34    }
35}
36
37impl<S, T, Req, BE, BD, Err> Transform<S, Req> for ConditionOption<T>
38where
39    S: Service<Req, Response = ServiceResponse<BD>, Error = Err> + 'static,
40    T: Transform<S, Req, Response = ServiceResponse<BE>, Error = Err>,
41    T::Future: 'static,
42    T::InitError: 'static,
43    T::Transform: 'static,
44{
45    type Response = ServiceResponse<EitherBody<BE, BD>>;
46    type Error = Err;
47    type Transform = ConditionMiddleware<T::Transform, S>;
48    type InitError = T::InitError;
49    type Future = LocalBoxFuture<'static, Result<Self::Transform, Self::InitError>>;
50
51    fn new_transform(&self, service: S) -> Self::Future {
52        match &self.inner {
53            Some(transformer) => {
54                let fut = transformer.new_transform(service);
55                async move {
56                    let wrapped_svc = fut.await?;
57                    Ok(ConditionMiddleware::Enable(wrapped_svc))
58                }
59                .boxed_local()
60            }
61            None => async move { Ok(ConditionMiddleware::Disable(service)) }.boxed_local(),
62        }
63    }
64}
65
66/// TODO
67#[derive(Debug)]
68pub enum ConditionMiddleware<E, D> {
69    Enable(E),
70    Disable(D),
71}
72
73impl<E, D, Req, BE, BD, Err> Service<Req> for ConditionMiddleware<E, D>
74where
75    E: Service<Req, Response = ServiceResponse<BE>, Error = Err>,
76    D: Service<Req, Response = ServiceResponse<BD>, Error = Err>,
77{
78    type Response = ServiceResponse<EitherBody<BE, BD>>;
79    type Error = Err;
80    type Future = ConditionMiddlewareFuture<E::Future, D::Future>;
81
82    fn poll_ready(&self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
83        match self {
84            ConditionMiddleware::Enable(service) => service.poll_ready(cx),
85            ConditionMiddleware::Disable(service) => service.poll_ready(cx),
86        }
87    }
88
89    fn call(&self, req: Req) -> Self::Future {
90        match self {
91            ConditionMiddleware::Enable(service) => ConditionMiddlewareFuture::Enabled {
92                fut: service.call(req),
93            },
94            ConditionMiddleware::Disable(service) => ConditionMiddlewareFuture::Disabled {
95                fut: service.call(req),
96            },
97        }
98    }
99}
100
101pin_project! {
102    #[doc(hidden)]
103    #[project = ConditionProj]
104    pub enum ConditionMiddlewareFuture<E, D> {
105        Enabled { #[pin] fut: E, },
106        Disabled { #[pin] fut: D, },
107    }
108}
109
110impl<E, D, BE, BD, Err> Future for ConditionMiddlewareFuture<E, D>
111where
112    E: Future<Output = Result<ServiceResponse<BE>, Err>>,
113    D: Future<Output = Result<ServiceResponse<BD>, Err>>,
114{
115    type Output = Result<ServiceResponse<EitherBody<BE, BD>>, Err>;
116
117    #[inline]
118    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
119        let res = match self.project() {
120            ConditionProj::Enabled { fut } => ready!(fut.poll(cx))?.map_into_left_body(),
121            ConditionProj::Disabled { fut } => ready!(fut.poll(cx))?.map_into_right_body(),
122        };
123
124        Poll::Ready(Ok(res))
125    }
126}