Skip to main content

tower_fallback/
service.rs

1use std::task::{Context, Poll};
2
3use tower::Service;
4
5use super::future::ResponseFuture;
6use crate::BoxedError;
7
8/// Decides if a [`Fallback`] service should call its fallback service.
9pub trait FallbackPolicy<Response> {
10    /// Returns `true` if the fallback service should handle this request.
11    fn should_fallback(&self, result: &Result<Response, BoxedError>) -> bool;
12}
13
14impl<Response, F> FallbackPolicy<Response> for F
15where
16    F: Fn(&Result<Response, BoxedError>) -> bool,
17{
18    fn should_fallback(&self, result: &Result<Response, BoxedError>) -> bool {
19        self(result)
20    }
21}
22
23/// The default fallback policy.
24///
25/// Falls back whenever the first service returns an error.
26#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
27pub struct OnError;
28
29impl<Response> FallbackPolicy<Response> for OnError {
30    fn should_fallback(&self, result: &Result<Response, BoxedError>) -> bool {
31        result.is_err()
32    }
33}
34
35/// Provides fallback processing on a second service if its fallback policy selects it.
36#[derive(Debug)]
37pub struct Fallback<S1, S2, F = OnError>
38where
39    S2: Clone,
40{
41    svc1: S1,
42    svc2: S2,
43    policy: F,
44}
45
46impl<S1: Clone, S2: Clone, F: Clone> Clone for Fallback<S1, S2, F> {
47    fn clone(&self) -> Self {
48        Self {
49            svc1: self.svc1.clone(),
50            svc2: self.svc2.clone(),
51            policy: self.policy.clone(),
52        }
53    }
54}
55
56impl<S1, S2: Clone> Fallback<S1, S2, OnError> {
57    /// Creates a new `Fallback` wrapping a pair of services.
58    ///
59    /// Requests are processed on `svc1`, and retried on `svc2` if `svc1` errored.
60    pub fn new(svc1: S1, svc2: S2) -> Self {
61        Self {
62            svc1,
63            svc2,
64            policy: OnError,
65        }
66    }
67}
68
69impl<S1, S2: Clone, F: Clone> Fallback<S1, S2, F> {
70    /// Creates a new `Fallback` wrapping a pair of services with a custom fallback policy.
71    ///
72    /// Requests are processed on `svc1`, and retried on `svc2` if `policy` returns `true`
73    /// for `svc1`'s result.
74    pub fn new_with_policy(svc1: S1, svc2: S2, policy: F) -> Self {
75        Self { svc1, svc2, policy }
76    }
77}
78
79impl<S1, S2, F, Request> Service<Request> for Fallback<S1, S2, F>
80where
81    S1: Service<Request>,
82    S2: Service<Request, Response = <S1 as Service<Request>>::Response>,
83    F: FallbackPolicy<<S1 as Service<Request>>::Response> + Clone,
84    S1::Error: Into<BoxedError>,
85    S2::Error: Into<BoxedError>,
86    S2: Clone,
87    Request: Clone,
88{
89    type Response = <S1 as Service<Request>>::Response;
90    type Error = BoxedError;
91    type Future = ResponseFuture<S1, S2, Request, F>;
92
93    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
94        self.svc1.poll_ready(cx).map_err(Into::into)
95    }
96
97    fn call(&mut self, request: Request) -> Self::Future {
98        let request2 = request.clone();
99        ResponseFuture::new(
100            self.svc1.call(request),
101            request2,
102            self.svc2.clone(),
103            self.policy.clone(),
104        )
105    }
106}