Skip to main content

tower/util/
optional_layer.rs

1//! A [`Layer`] that is enabled or disabled by an [`Option`].
2//!
3//! See [`OptionLayer`] and [`option_layer`] for more details.
4//!
5//! [`option_layer`]: crate::util::option_layer
6
7use crate::BoxError;
8use pin_project_lite::pin_project;
9use std::{
10    future::Future,
11    pin::Pin,
12    task::{Context, Poll},
13};
14use tower_layer::Layer;
15use tower_service::Service;
16
17/// A [`Layer`] that optionally applies an inner layer `L`.
18///
19/// This is the layer produced by [`option_layer`]. When the inner layer is
20/// present, the resulting service is the layered service; when it is absent,
21/// the resulting service is the unmodified service.
22///
23/// Unlike branching with [`Either`] directly, [`OptionLayer`] unifies the error
24/// types of the two branches to [`BoxError`]. This means the optional layer is
25/// allowed to change the error type (as [`TimeoutLayer`] does, for example)
26/// without the two branches needing to share an error type.
27///
28/// [`option_layer`]: crate::util::option_layer
29/// [`Either`]: crate::util::Either
30/// [`BoxError`]: crate::BoxError
31/// [`TimeoutLayer`]: crate::timeout::TimeoutLayer
32#[derive(Clone, Copy, Debug)]
33pub struct OptionLayer<L> {
34    layer: Option<L>,
35}
36
37impl<L> OptionLayer<L> {
38    /// Create a new [`OptionLayer`] wrapping the given optional layer.
39    pub const fn new(layer: Option<L>) -> Self {
40        OptionLayer { layer }
41    }
42}
43
44impl<L> From<Option<L>> for OptionLayer<L> {
45    fn from(layer: Option<L>) -> Self {
46        OptionLayer::new(layer)
47    }
48}
49
50impl<S, L> Layer<S> for OptionLayer<L>
51where
52    L: Layer<S>,
53{
54    type Service = OptionService<L::Service, S>;
55
56    fn layer(&self, inner: S) -> Self::Service {
57        match &self.layer {
58            Some(layer) => OptionService::Some(layer.layer(inner)),
59            None => OptionService::None(inner),
60        }
61    }
62}
63
64/// The [`Service`] produced by [`OptionLayer`].
65///
66/// Its error type is [`BoxError`], erasing any difference between the layered
67/// and unlayered branches' error types.
68///
69/// [`BoxError`]: crate::BoxError
70#[derive(Clone, Copy, Debug)]
71pub enum OptionService<A, B> {
72    /// The inner layer was present; the layered service.
73    Some(A),
74    /// The inner layer was absent; the unmodified service.
75    None(B),
76}
77
78impl<A, B, Request> Service<Request> for OptionService<A, B>
79where
80    A: Service<Request>,
81    A::Error: Into<BoxError>,
82    B: Service<Request, Response = A::Response>,
83    B::Error: Into<BoxError>,
84{
85    type Response = A::Response;
86    type Error = BoxError;
87    type Future = ResponseFuture<A::Future, B::Future>;
88
89    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
90        match self {
91            OptionService::Some(service) => service.poll_ready(cx).map_err(Into::into),
92            OptionService::None(service) => service.poll_ready(cx).map_err(Into::into),
93        }
94    }
95
96    fn call(&mut self, request: Request) -> Self::Future {
97        match self {
98            OptionService::Some(service) => ResponseFuture {
99                kind: Kind::Some {
100                    inner: service.call(request),
101                },
102            },
103            OptionService::None(service) => ResponseFuture {
104                kind: Kind::None {
105                    inner: service.call(request),
106                },
107            },
108        }
109    }
110}
111
112pin_project! {
113    /// Response future for [`OptionService`].
114    pub struct ResponseFuture<A, B> {
115        #[pin]
116        kind: Kind<A, B>,
117    }
118}
119
120pin_project! {
121    #[project = KindProj]
122    enum Kind<A, B> {
123        Some { #[pin] inner: A },
124        None { #[pin] inner: B },
125    }
126}
127
128impl<A, B, T, AE, BE> Future for ResponseFuture<A, B>
129where
130    A: Future<Output = Result<T, AE>>,
131    AE: Into<BoxError>,
132    B: Future<Output = Result<T, BE>>,
133    BE: Into<BoxError>,
134{
135    type Output = Result<T, BoxError>;
136
137    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
138        match self.project().kind.project() {
139            KindProj::Some { inner } => inner.poll(cx).map_err(Into::into),
140            KindProj::None { inner } => inner.poll(cx).map_err(Into::into),
141        }
142    }
143}