1 //! Contains `Either` and related types and functions. 2 //! 3 //! See `Either` documentation for more details. 4 5 use futures_core::ready; 6 use pin_project::{pin_project, project}; 7 use std::{ 8 future::Future, 9 pin::Pin, 10 task::{Context, Poll}, 11 }; 12 use tower_service::Service; 13 14 /// Combine two different service types into a single type. 15 /// 16 /// Both services must be of the same request, response, and error types. 17 /// `Either` is useful for handling conditional branching in service middleware 18 /// to different inner service types. 19 #[pin_project] 20 #[derive(Clone, Debug)] 21 pub enum Either<A, B> { 22 /// One type of backing `Service`. 23 A(#[pin] A), 24 /// The other type of backing `Service`. 25 B(#[pin] B), 26 } 27 28 type Error = Box<dyn std::error::Error + Send + Sync>; 29 30 impl<A, B, Request> Service<Request> for Either<A, B> 31 where 32 A: Service<Request>, 33 A::Error: Into<Error>, 34 B: Service<Request, Response = A::Response>, 35 B::Error: Into<Error>, 36 { 37 type Response = A::Response; 38 type Error = Error; 39 type Future = Either<A::Future, B::Future>; 40 poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>41 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { 42 use self::Either::*; 43 44 match self { 45 A(service) => Poll::Ready(Ok(ready!(service.poll_ready(cx)).map_err(Into::into)?)), 46 B(service) => Poll::Ready(Ok(ready!(service.poll_ready(cx)).map_err(Into::into)?)), 47 } 48 } 49 call(&mut self, request: Request) -> Self::Future50 fn call(&mut self, request: Request) -> Self::Future { 51 use self::Either::*; 52 53 match self { 54 A(service) => A(service.call(request)), 55 B(service) => B(service.call(request)), 56 } 57 } 58 } 59 60 impl<A, B, T, AE, BE> Future for Either<A, B> 61 where 62 A: Future<Output = Result<T, AE>>, 63 AE: Into<Error>, 64 B: Future<Output = Result<T, BE>>, 65 BE: Into<Error>, 66 { 67 type Output = Result<T, Error>; 68 69 #[project] poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>70 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { 71 #[project] 72 match self.project() { 73 Either::A(fut) => Poll::Ready(Ok(ready!(fut.poll(cx)).map_err(Into::into)?)), 74 Either::B(fut) => Poll::Ready(Ok(ready!(fut.poll(cx)).map_err(Into::into)?)), 75 } 76 } 77 } 78