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