actix_web_lab/
condition_option.rs1use 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#[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#[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}