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_util::{FutureExt as _, future::LocalBoxFuture};
13use pin_project_lite::pin_project;
14
15#[derive(Debug)]
26pub struct ConditionOption<T> {
27 inner: Option<T>,
28}
29
30impl<T> From<Option<T>> for ConditionOption<T> {
31 fn from(value: Option<T>) -> Self {
32 Self { inner: value }
33 }
34}
35
36impl<S, T, Req, BE, BD, Err> Transform<S, Req> for ConditionOption<T>
37where
38 S: Service<Req, Response = ServiceResponse<BD>, Error = Err> + 'static,
39 T: Transform<S, Req, Response = ServiceResponse<BE>, Error = Err>,
40 T::Future: 'static,
41 T::InitError: 'static,
42 T::Transform: 'static,
43{
44 type Response = ServiceResponse<EitherBody<BE, BD>>;
45 type Error = Err;
46 type Transform = ConditionMiddleware<T::Transform, S>;
47 type InitError = T::InitError;
48 type Future = LocalBoxFuture<'static, Result<Self::Transform, Self::InitError>>;
49
50 fn new_transform(&self, service: S) -> Self::Future {
51 match &self.inner {
52 Some(transformer) => {
53 let fut = transformer.new_transform(service);
54 async move {
55 let wrapped_svc = fut.await?;
56 Ok(ConditionMiddleware::Enable(wrapped_svc))
57 }
58 .boxed_local()
59 }
60 None => async move { Ok(ConditionMiddleware::Disable(service)) }.boxed_local(),
61 }
62 }
63}
64
65#[derive(Debug)]
67pub enum ConditionMiddleware<E, D> {
68 Enable(E),
69 Disable(D),
70}
71
72impl<E, D, Req, BE, BD, Err> Service<Req> for ConditionMiddleware<E, D>
73where
74 E: Service<Req, Response = ServiceResponse<BE>, Error = Err>,
75 D: Service<Req, Response = ServiceResponse<BD>, Error = Err>,
76{
77 type Response = ServiceResponse<EitherBody<BE, BD>>;
78 type Error = Err;
79 type Future = ConditionMiddlewareFuture<E::Future, D::Future>;
80
81 fn poll_ready(&self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
82 match self {
83 ConditionMiddleware::Enable(service) => service.poll_ready(cx),
84 ConditionMiddleware::Disable(service) => service.poll_ready(cx),
85 }
86 }
87
88 fn call(&self, req: Req) -> Self::Future {
89 match self {
90 ConditionMiddleware::Enable(service) => ConditionMiddlewareFuture::Enabled {
91 fut: service.call(req),
92 },
93 ConditionMiddleware::Disable(service) => ConditionMiddlewareFuture::Disabled {
94 fut: service.call(req),
95 },
96 }
97 }
98}
99
100pin_project! {
101 #[doc(hidden)]
102 #[project = ConditionProj]
103 pub enum ConditionMiddlewareFuture<E, D> {
104 Enabled { #[pin] fut: E, },
105 Disabled { #[pin] fut: D, },
106 }
107}
108
109impl<E, D, BE, BD, Err> Future for ConditionMiddlewareFuture<E, D>
110where
111 E: Future<Output = Result<ServiceResponse<BE>, Err>>,
112 D: Future<Output = Result<ServiceResponse<BD>, Err>>,
113{
114 type Output = Result<ServiceResponse<EitherBody<BE, BD>>, Err>;
115
116 #[inline]
117 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
118 let res = match self.project() {
119 ConditionProj::Enabled { fut } => ready!(fut.poll(cx))?.map_into_left_body(),
120 ConditionProj::Disabled { fut } => ready!(fut.poll(cx))?.map_into_right_body(),
121 };
122
123 Poll::Ready(Ok(res))
124 }
125}