Skip to main content

tower_resilience_fallback/
config.rs

1//! Configuration for the fallback service.
2
3use crate::{FallbackEvent, FallbackStrategy, HandlePredicate, HandleResponsePredicate};
4use tower_resilience_core::{EventListeners, FnListener};
5
6/// Configuration for the fallback service.
7pub struct FallbackConfig<Req, Res, E> {
8    pub(crate) name: String,
9    pub(crate) strategy: FallbackStrategy<Req, Res, E>,
10    pub(crate) handle_predicate: Option<HandlePredicate<E>>,
11    pub(crate) handle_response_predicate: Option<HandleResponsePredicate<Res>>,
12    pub(crate) event_listeners: EventListeners<FallbackEvent>,
13}
14
15/// Builder for constructing a [`FallbackLayer`](crate::FallbackLayer).
16pub struct FallbackConfigBuilder<Req, Res, E> {
17    name: String,
18    strategy: Option<FallbackStrategy<Req, Res, E>>,
19    handle_predicate: Option<HandlePredicate<E>>,
20    handle_response_predicate: Option<HandleResponsePredicate<Res>>,
21    event_listeners: EventListeners<FallbackEvent>,
22}
23
24impl<Req, Res, E> Default for FallbackConfigBuilder<Req, Res, E> {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl<Req, Res, E> FallbackConfigBuilder<Req, Res, E> {
31    /// Creates a new builder with default settings.
32    pub fn new() -> Self {
33        Self {
34            name: "fallback".to_string(),
35            strategy: None,
36            handle_predicate: None,
37            handle_response_predicate: None,
38            event_listeners: EventListeners::new(),
39        }
40    }
41
42    /// Sets the name for this fallback instance (used in metrics and events).
43    pub fn name(mut self, name: impl Into<String>) -> Self {
44        self.name = name.into();
45        self
46    }
47
48    /// Sets a static fallback value.
49    ///
50    /// Note: This requires `Res: Clone`. If your response type doesn't implement
51    /// Clone, use [`value_fn`](Self::value_fn) instead.
52    pub fn value(mut self, value: Res) -> Self
53    where
54        Res: Clone,
55    {
56        self.strategy = Some(FallbackStrategy::Value(value));
57        self
58    }
59
60    /// Sets a fallback value generator function.
61    ///
62    /// Unlike [`value`](Self::value), this doesn't require `Res: Clone` since
63    /// the function generates a fresh value for each fallback.
64    ///
65    /// # Example
66    ///
67    /// ```rust
68    /// use tower_resilience_fallback::FallbackLayer;
69    ///
70    /// # #[derive(Debug)]
71    /// # struct MyError;
72    /// # struct MyResponse { data: Vec<u8> }  // No Clone!
73    /// let layer: FallbackLayer<String, MyResponse, MyError> = FallbackLayer::builder()
74    ///     .value_fn(|| MyResponse { data: vec![0; 1024] })
75    ///     .build();
76    /// ```
77    pub fn value_fn<F>(mut self, f: F) -> Self
78    where
79        F: Fn() -> Res + Send + Sync + 'static,
80    {
81        self.strategy = Some(FallbackStrategy::ValueFn(std::sync::Arc::new(f)));
82        self
83    }
84
85    /// Sets a fallback function that computes a response from the error.
86    pub fn from_error<F>(mut self, f: F) -> Self
87    where
88        F: Fn(&E) -> Res + Send + Sync + 'static,
89    {
90        self.strategy = Some(FallbackStrategy::FromError(std::sync::Arc::new(f)));
91        self
92    }
93
94    /// Sets a fallback function that has access to both request and error.
95    pub fn from_request_error<F>(mut self, f: F) -> Self
96    where
97        F: Fn(&Req, &E) -> Res + Send + Sync + 'static,
98    {
99        self.strategy = Some(FallbackStrategy::FromRequestError(std::sync::Arc::new(f)));
100        self
101    }
102
103    /// Sets a backup service to call on failure.
104    pub fn service<S, Fut>(mut self, service: S) -> Self
105    where
106        S: Fn(Req) -> Fut + Send + Sync + 'static,
107        Fut: std::future::Future<Output = Result<Res, E>> + Send + 'static,
108    {
109        self.strategy = Some(FallbackStrategy::Service(std::sync::Arc::new(move |req| {
110            Box::pin(service(req))
111        })));
112        self
113    }
114
115    /// Sets an error transformation function.
116    pub fn exception<F>(mut self, f: F) -> Self
117    where
118        F: Fn(E) -> E + Send + Sync + 'static,
119    {
120        self.strategy = Some(FallbackStrategy::Exception(std::sync::Arc::new(f)));
121        self
122    }
123
124    /// Only trigger fallback for errors matching this predicate.
125    ///
126    /// Errors that don't match the predicate will be propagated as-is.
127    pub fn handle<F>(mut self, predicate: F) -> Self
128    where
129        F: Fn(&E) -> bool + Send + Sync + 'static,
130    {
131        self.handle_predicate = Some(std::sync::Arc::new(predicate));
132        self
133    }
134
135    /// Trigger fallback for successful responses matching this predicate.
136    ///
137    /// When this predicate returns `true` for a response, the fallback strategy
138    /// is applied as if the service had returned an error. This is useful when
139    /// errors are encoded inside successful responses, such as:
140    /// - JSON-RPC error codes in the response body
141    /// - HTTP 200 responses containing error payloads
142    /// - Responses indicating degraded or stale data
143    ///
144    /// # Example
145    ///
146    /// ```rust
147    /// use tower_resilience_fallback::FallbackLayer;
148    ///
149    /// #[derive(Debug, Clone)]
150    /// struct MyError;
151    ///
152    /// #[derive(Clone)]
153    /// struct ApiResponse {
154    ///     is_stale: bool,
155    ///     data: String,
156    /// }
157    ///
158    /// let layer: FallbackLayer<String, ApiResponse, MyError> = FallbackLayer::builder()
159    ///     .value_fn(|| ApiResponse { is_stale: false, data: "default".to_string() })
160    ///     .handle_response(|resp: &ApiResponse| resp.is_stale)
161    ///     .build();
162    /// ```
163    pub fn handle_response<F>(mut self, predicate: F) -> Self
164    where
165        F: Fn(&Res) -> bool + Send + Sync + 'static,
166    {
167        self.handle_response_predicate = Some(std::sync::Arc::new(predicate));
168        self
169    }
170
171    /// Adds an event listener.
172    pub fn on_event<F>(mut self, listener: F) -> Self
173    where
174        F: Fn(&FallbackEvent) + Send + Sync + 'static,
175    {
176        self.event_listeners.add(FnListener::new(listener));
177        self
178    }
179
180    /// Builds the fallback layer.
181    ///
182    /// # Panics
183    ///
184    /// Panics if no fallback strategy was configured.
185    pub fn build(self) -> crate::FallbackLayer<Req, Res, E> {
186        let config = FallbackConfig {
187            name: self.name,
188            strategy: self.strategy.expect("fallback strategy must be set"),
189            handle_predicate: self.handle_predicate,
190            handle_response_predicate: self.handle_response_predicate,
191            event_listeners: self.event_listeners,
192        };
193        crate::FallbackLayer::new(config)
194    }
195}