//! Contains `Either` service and related types and functions. use std::pin::Pin; use std::task::{Context, Poll}; use actix_service::{Service, ServiceFactory}; use futures::{future, ready, Future}; /// Combine two different service types into a single type. /// /// Both services must be of the same request, response, and error types. /// `EitherService` is useful for handling conditional branching in service /// middleware to different inner service types. pub struct EitherService { left: A, right: B, } impl Clone for EitherService { fn clone(&self) -> Self { EitherService { left: self.left.clone(), right: self.right.clone(), } } } impl Service for EitherService where A: Service, B: Service, { type Request = either::Either; type Response = A::Response; type Error = A::Error; type Future = future::Either; fn poll_ready(&mut self, cx: &mut Context) -> Poll> { let left = self.left.poll_ready(cx)?; let right = self.right.poll_ready(cx)?; if left.is_ready() && right.is_ready() { Poll::Ready(Ok(())) } else { Poll::Pending } } fn call(&mut self, req: either::Either) -> Self::Future { match req { either::Either::Left(req) => future::Either::Left(self.left.call(req)), either::Either::Right(req) => future::Either::Right(self.right.call(req)), } } } /// Combine two different new service types into a single service. pub struct Either { left: A, right: B, } impl Either { pub fn new(left: A, right: B) -> Either where A: ServiceFactory, B: ServiceFactory< Config = A::Config, Response = A::Response, Error = A::Error, InitError = A::InitError, >, { Either { left, right } } } impl ServiceFactory for Either where A: ServiceFactory, B: ServiceFactory< Config = A::Config, Response = A::Response, Error = A::Error, InitError = A::InitError, >, A::Future: Unpin, B::Future: Unpin, { type Request = either::Either; type Response = A::Response; type Error = A::Error; type InitError = A::InitError; type Config = A::Config; type Service = EitherService; type Future = EitherNewService; fn new_service(&self, cfg: &A::Config) -> Self::Future { EitherNewService { left: None, right: None, left_fut: self.left.new_service(cfg), right_fut: self.right.new_service(cfg), } } } impl Clone for Either { fn clone(&self) -> Self { Self { left: self.left.clone(), right: self.right.clone(), } } } #[doc(hidden)] pub struct EitherNewService { left: Option, right: Option, left_fut: A::Future, right_fut: B::Future, } impl Unpin for EitherNewService where A: ServiceFactory, B: ServiceFactory, A::Future: Unpin, B::Future: Unpin, { } impl Future for EitherNewService where A: ServiceFactory, B: ServiceFactory, A::Future: Unpin, B::Future: Unpin, { type Output = Result, A::InitError>; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { let this = self.get_mut(); if this.left.is_none() { this.left = Some(ready!(Pin::new(&mut this.left_fut).poll(cx))?); } if this.right.is_none() { this.right = Some(ready!(Pin::new(&mut this.right_fut).poll(cx))?); } if this.left.is_some() && this.right.is_some() { Poll::Ready(Ok(EitherService { left: this.left.take().unwrap(), right: this.right.take().unwrap(), })) } else { Poll::Pending } } }