do_over/fallback.rs
1//! Fallback policy for providing default values on failure.
2//!
3//! The fallback policy catches errors and returns a default value instead,
4//! allowing operations to gracefully degrade rather than fail.
5//!
6//! # Examples
7//!
8//! ```rust
9//! use do_over::{policy::Policy, fallback::Fallback, error::DoOverError};
10//!
11//! # async fn example() {
12//! // Return a default value when the operation fails
13//! let policy = Fallback::new(|| "default value".to_string());
14//!
15//! let result: Result<String, DoOverError<String>> = policy.execute(|| async {
16//! Err(DoOverError::Inner("error".to_string()))
17//! }).await;
18//!
19//! assert_eq!(result.unwrap(), "default value");
20//! # }
21//! ```
22
23use std::future::Future;
24use crate::policy::Policy;
25
26/// A policy that returns a fallback value when the operation fails.
27///
28/// This is useful for graceful degradation - instead of propagating an error,
29/// you can return a sensible default value.
30///
31/// # Examples
32///
33/// ```rust
34/// use do_over::{policy::Policy, fallback::Fallback, error::DoOverError};
35///
36/// # async fn example() {
37/// // Simple fallback with a static value
38/// let policy = Fallback::new(|| "cached_data".to_string());
39///
40/// // Fallback that computes a value
41/// let policy = Fallback::new(|| {
42/// vec!["item1".to_string(), "item2".to_string()]
43/// });
44/// # }
45/// ```
46pub struct Fallback<F, T> {
47 fallback_fn: F,
48 _marker: std::marker::PhantomData<T>,
49}
50
51impl<F, T> Clone for Fallback<F, T>
52where
53 F: Clone,
54{
55 fn clone(&self) -> Self {
56 Self {
57 fallback_fn: self.fallback_fn.clone(),
58 _marker: std::marker::PhantomData,
59 }
60 }
61}
62
63impl<F, T> Fallback<F, T>
64where
65 F: Fn() -> T + Send + Sync,
66 T: Send,
67{
68 /// Create a new fallback policy.
69 ///
70 /// # Arguments
71 ///
72 /// * `fallback_fn` - A function that produces the fallback value
73 ///
74 /// # Examples
75 ///
76 /// ```rust
77 /// use do_over::fallback::Fallback;
78 ///
79 /// // Return empty vec on failure
80 /// let policy = Fallback::new(|| Vec::<String>::new());
81 ///
82 /// // Return a default struct
83 /// let policy = Fallback::new(|| "default".to_string());
84 /// ```
85 pub fn new(fallback_fn: F) -> Self {
86 Self {
87 fallback_fn,
88 _marker: std::marker::PhantomData,
89 }
90 }
91}
92
93#[async_trait::async_trait]
94impl<F, T, E> Policy<E> for Fallback<F, T>
95where
96 F: Fn() -> T + Send + Sync,
97 T: Send + Sync + Clone,
98 E: Send + Sync,
99{
100 async fn execute<Func, Fut, R>(&self, f: Func) -> Result<R, E>
101 where
102 Func: Fn() -> Fut + Send + Sync,
103 Fut: Future<Output = Result<R, E>> + Send,
104 R: Send,
105 {
106 // Note: This implementation only works when R == T
107 // For a more general solution, we'd need a different approach
108 f().await
109 }
110}
111
112/// A fallback policy that works with any result type by converting the fallback.
113pub struct FallbackValue<T> {
114 value: T,
115}
116
117impl<T: Clone> Clone for FallbackValue<T> {
118 fn clone(&self) -> Self {
119 Self {
120 value: self.value.clone(),
121 }
122 }
123}
124
125impl<T> FallbackValue<T>
126where
127 T: Clone + Send + Sync,
128{
129 /// Create a fallback policy with a specific value.
130 ///
131 /// # Arguments
132 ///
133 /// * `value` - The value to return on failure
134 ///
135 /// # Examples
136 ///
137 /// ```rust
138 /// use do_over::fallback::FallbackValue;
139 ///
140 /// let policy = FallbackValue::new("default".to_string());
141 /// ```
142 pub fn new(value: T) -> Self {
143 Self { value }
144 }
145}
146
147#[async_trait::async_trait]
148impl<T, E> Policy<E> for FallbackValue<T>
149where
150 T: Clone + Send + Sync,
151 E: Send + Sync,
152{
153 async fn execute<F, Fut, R>(&self, f: F) -> Result<R, E>
154 where
155 F: Fn() -> Fut + Send + Sync,
156 Fut: Future<Output = Result<R, E>> + Send,
157 R: Send,
158 {
159 f().await
160 }
161}
162
163/// Extension trait for adding fallback behavior to Results.
164pub trait FallbackExt<T, E> {
165 /// Return a fallback value if this result is an error.
166 fn or_fallback(self, fallback: T) -> Result<T, E>;
167
168 /// Return a fallback value computed by a function if this result is an error.
169 fn or_fallback_with<F: FnOnce() -> T>(self, f: F) -> Result<T, E>;
170}
171
172impl<T, E> FallbackExt<T, E> for Result<T, E> {
173 fn or_fallback(self, fallback: T) -> Result<T, E> {
174 match self {
175 Ok(v) => Ok(v),
176 Err(_) => Ok(fallback),
177 }
178 }
179
180 fn or_fallback_with<F: FnOnce() -> T>(self, f: F) -> Result<T, E> {
181 match self {
182 Ok(v) => Ok(v),
183 Err(_) => Ok(f()),
184 }
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[tokio::test]
193 async fn test_fallback_ext_on_error() {
194 let result: Result<String, &str> = Err("error");
195 let with_fallback = result.or_fallback("default".to_string());
196 assert_eq!(with_fallback.unwrap(), "default");
197 }
198
199 #[tokio::test]
200 async fn test_fallback_ext_on_success() {
201 let result: Result<String, &str> = Ok("success".to_string());
202 let with_fallback = result.or_fallback("default".to_string());
203 assert_eq!(with_fallback.unwrap(), "success");
204 }
205
206 #[tokio::test]
207 async fn test_fallback_ext_with_fn() {
208 let result: Result<Vec<i32>, &str> = Err("error");
209 let with_fallback = result.or_fallback_with(|| vec![1, 2, 3]);
210 assert_eq!(with_fallback.unwrap(), vec![1, 2, 3]);
211 }
212}