aws_smithy_http_server/plugin/
either.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Contains the [`Either`] enum.
7
8use pin_project_lite::pin_project;
9use std::{
10    future::Future,
11    pin::Pin,
12    task::{Context, Poll},
13};
14use tower::{Layer, Service};
15
16use super::Plugin;
17
18// TODO(https://github.com/smithy-lang/smithy-rs/pull/2441#pullrequestreview-1331345692): Seems like
19// this type should land in `tower-0.5`.
20pin_project! {
21    /// Combine two different [`Futures`](std::future::Future)/[`Services`](tower::Service)/
22    /// [`Layers`](tower::Layer)/[`Plugins`](super::Plugin) into a single type.
23    ///
24    /// # Notes on [`Future`](std::future::Future)
25    ///
26    /// The [`Future::Output`] must be identical.
27    ///
28    /// # Notes on [`Service`](tower::Service)
29    ///
30    /// The [`Service::Response`] and [`Service::Error`] must be identical.
31    #[derive(Clone, Debug)]
32    #[project = EitherProj]
33    pub enum Either<L, R> {
34        /// One type of backing [`Service`].
35        Left { #[pin] value: L },
36        /// The other type of backing [`Service`].
37        Right { #[pin] value: R },
38    }
39}
40
41impl<L, R> Future for Either<L, R>
42where
43    L: Future,
44    R: Future<Output = L::Output>,
45{
46    type Output = L::Output;
47
48    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
49        match self.project() {
50            EitherProj::Left { value } => value.poll(cx),
51            EitherProj::Right { value } => value.poll(cx),
52        }
53    }
54}
55
56impl<L, R, Request> Service<Request> for Either<L, R>
57where
58    L: Service<Request>,
59    R: Service<Request, Response = L::Response, Error = L::Error>,
60{
61    type Response = L::Response;
62    type Error = L::Error;
63    type Future = Either<L::Future, R::Future>;
64
65    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
66        use self::Either::*;
67
68        match self {
69            Left { value } => value.poll_ready(cx),
70            Right { value } => value.poll_ready(cx),
71        }
72    }
73
74    fn call(&mut self, request: Request) -> Self::Future {
75        use self::Either::*;
76
77        match self {
78            Left { value } => Either::Left {
79                value: value.call(request),
80            },
81            Right { value } => Either::Right {
82                value: value.call(request),
83            },
84        }
85    }
86}
87
88impl<S, L, R> Layer<S> for Either<L, R>
89where
90    L: Layer<S>,
91    R: Layer<S>,
92{
93    type Service = Either<L::Service, R::Service>;
94
95    fn layer(&self, inner: S) -> Self::Service {
96        match self {
97            Either::Left { value } => Either::Left {
98                value: value.layer(inner),
99            },
100            Either::Right { value } => Either::Right {
101                value: value.layer(inner),
102            },
103        }
104    }
105}
106
107impl<Ser, Op, T, Le, Ri> Plugin<Ser, Op, T> for Either<Le, Ri>
108where
109    Le: Plugin<Ser, Op, T>,
110    Ri: Plugin<Ser, Op, T>,
111{
112    type Output = Either<Le::Output, Ri::Output>;
113
114    fn apply(&self, input: T) -> Self::Output {
115        match self {
116            Either::Left { value } => Either::Left {
117                value: value.apply(input),
118            },
119            Either::Right { value } => Either::Right {
120                value: value.apply(input),
121            },
122        }
123    }
124}