//! [`Or`] used to combine two services into one. use super::FromEmptyRouter; use crate::body::BoxBody; use futures_util::ready; use http::{Request, Response}; use pin_project_lite::pin_project; use std::{ convert::Infallible, future::Future, pin::Pin, task::{Context, Poll}, }; use tower::{util::Oneshot, ServiceExt}; use tower_service::Service; /// [`tower::Service`] that is the combination of two routers. /// /// See [`Router::or`] for more details. /// /// [`Router::or`]: super::Router::or #[derive(Debug, Clone, Copy)] pub(crate) struct Or { pub(super) first: A, pub(super) second: B, } #[test] fn traits() { use crate::tests::*; assert_send::>(); assert_sync::>(); } impl Service> for Or where A: Service, Response = Response, Error = Infallible> + Clone, B: Service, Response = Response, Error = Infallible> + Clone, ReqBody: Send + Sync + 'static, A: Send + 'static, B: Send + 'static, A::Future: Send + 'static, B::Future: Send + 'static, { type Response = Response; type Error = Infallible; type Future = ResponseFuture; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, req: Request) -> Self::Future { ResponseFuture { state: State::FirstFuture { f: self.first.clone().oneshot(req), }, second: Some(self.second.clone()), } } } pin_project! { /// Response future for [`Or`]. pub(crate) struct ResponseFuture where A: Service>, B: Service>, { #[pin] state: State, second: Option, } } pin_project! { #[project = StateProj] enum State where A: Service>, B: Service>, { FirstFuture { #[pin] f: Oneshot> }, SecondFuture { #[pin] f: Oneshot>, } } } impl Future for ResponseFuture where A: Service, Response = Response, Error = Infallible>, B: Service, Response = Response, Error = Infallible>, ReqBody: Send + Sync + 'static, { type Output = Result, Infallible>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { let mut this = self.as_mut().project(); let new_state = match this.state.as_mut().project() { StateProj::FirstFuture { f } => { let mut response = ready!(f.poll(cx)?); let req = if let Some(ext) = response .extensions_mut() .remove::>() { ext.request } else { return Poll::Ready(Ok(response)); }; let second = this.second.take().expect("future polled after completion"); State::SecondFuture { f: second.oneshot(req), } } StateProj::SecondFuture { f } => return f.poll(cx), }; this.state.set(new_state); } } }