scrappy_utils/
either.rs

1//! Contains `Either` service and related types and functions.
2use std::pin::Pin;
3use std::task::{Context, Poll};
4
5use scrappy_service::{Service, ServiceFactory};
6use futures::{future, ready, Future};
7
8/// Combine two different service types into a single type.
9///
10/// Both services must be of the same request, response, and error types.
11/// `EitherService` is useful for handling conditional branching in service
12/// middleware to different inner service types.
13pub struct EitherService<A, B> {
14    left: A,
15    right: B,
16}
17
18impl<A: Clone, B: Clone> Clone for EitherService<A, B> {
19    fn clone(&self) -> Self {
20        EitherService {
21            left: self.left.clone(),
22            right: self.right.clone(),
23        }
24    }
25}
26
27impl<A, B> Service for EitherService<A, B>
28where
29    A: Service,
30    B: Service<Response = A::Response, Error = A::Error>,
31{
32    type Request = either::Either<A::Request, B::Request>;
33    type Response = A::Response;
34    type Error = A::Error;
35    type Future = future::Either<A::Future, B::Future>;
36
37    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
38        let left = self.left.poll_ready(cx)?;
39        let right = self.right.poll_ready(cx)?;
40
41        if left.is_ready() && right.is_ready() {
42            Poll::Ready(Ok(()))
43        } else {
44            Poll::Pending
45        }
46    }
47
48    fn call(&mut self, req: either::Either<A::Request, B::Request>) -> Self::Future {
49        match req {
50            either::Either::Left(req) => future::Either::Left(self.left.call(req)),
51            either::Either::Right(req) => future::Either::Right(self.right.call(req)),
52        }
53    }
54}
55
56/// Combine two different new service types into a single service.
57pub struct Either<A, B> {
58    left: A,
59    right: B,
60}
61
62impl<A, B> Either<A, B> {
63    pub fn new(left: A, right: B) -> Either<A, B>
64    where
65        A: ServiceFactory,
66        A::Config: Clone,
67        B: ServiceFactory<
68            Config = A::Config,
69            Response = A::Response,
70            Error = A::Error,
71            InitError = A::InitError,
72        >,
73    {
74        Either { left, right }
75    }
76}
77
78impl<A, B> ServiceFactory for Either<A, B>
79where
80    A: ServiceFactory,
81    A::Config: Clone,
82    B: ServiceFactory<
83        Config = A::Config,
84        Response = A::Response,
85        Error = A::Error,
86        InitError = A::InitError,
87    >,
88{
89    type Request = either::Either<A::Request, B::Request>;
90    type Response = A::Response;
91    type Error = A::Error;
92    type InitError = A::InitError;
93    type Config = A::Config;
94    type Service = EitherService<A::Service, B::Service>;
95    type Future = EitherNewService<A, B>;
96
97    fn new_service(&self, cfg: A::Config) -> Self::Future {
98        EitherNewService {
99            left: None,
100            right: None,
101            left_fut: self.left.new_service(cfg.clone()),
102            right_fut: self.right.new_service(cfg),
103        }
104    }
105}
106
107impl<A: Clone, B: Clone> Clone for Either<A, B> {
108    fn clone(&self) -> Self {
109        Self {
110            left: self.left.clone(),
111            right: self.right.clone(),
112        }
113    }
114}
115
116#[doc(hidden)]
117#[pin_project::pin_project]
118pub struct EitherNewService<A: ServiceFactory, B: ServiceFactory> {
119    left: Option<A::Service>,
120    right: Option<B::Service>,
121    #[pin]
122    left_fut: A::Future,
123    #[pin]
124    right_fut: B::Future,
125}
126
127impl<A, B> Future for EitherNewService<A, B>
128where
129    A: ServiceFactory,
130    B: ServiceFactory<Response = A::Response, Error = A::Error, InitError = A::InitError>,
131{
132    type Output = Result<EitherService<A::Service, B::Service>, A::InitError>;
133
134    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
135        let this = self.project();
136
137        if this.left.is_none() {
138            *this.left = Some(ready!(this.left_fut.poll(cx))?);
139        }
140        if this.right.is_none() {
141            *this.right = Some(ready!(this.right_fut.poll(cx))?);
142        }
143
144        if this.left.is_some() && this.right.is_some() {
145            Poll::Ready(Ok(EitherService {
146                left: this.left.take().unwrap(),
147                right: this.right.take().unwrap(),
148            }))
149        } else {
150            Poll::Pending
151        }
152    }
153}