error_rail/traits/error_ops.rs
1//! Operations for error recovery and transformation.
2//!
3//! This module provides [`ErrorOps`], a trait that extends [`WithError`] with
4//! additional combinators for recovering from errors and mapping both success
5//! and error cases simultaneously.
6//!
7//! # Examples
8//!
9//! ```
10//! use error_rail::traits::ErrorOps;
11//!
12//! let result: Result<i32, &str> = Err("failed");
13//! let recovered = result.recover(|_| Ok(42));
14//! assert_eq!(recovered, Ok(42));
15//! ```
16use crate::traits::with_error::WithError;
17
18/// Operations for error recovery and bidirectional mapping.
19///
20/// This trait provides methods to:
21/// - Recover from errors by providing a fallback computation
22/// - Transform both success and error cases in a single operation
23///
24/// # Type Parameters
25///
26/// * `E` - The error type contained in the implementor
27///
28/// # Examples
29///
30/// ```
31/// use error_rail::traits::ErrorOps;
32///
33/// let result: Result<i32, &str> = Err("error");
34/// let mapped = result.bimap_result(|x| x * 2, |e| format!("Error: {}", e));
35/// assert_eq!(mapped, Err("Error: error".to_string()));
36/// ```
37pub trait ErrorOps<E>: WithError<E> {
38 /// Attempts to recover from an error using the provided recovery function.
39 ///
40 /// If the value is an error, the recovery function is called with the error
41 /// and its result is returned. Otherwise, the success value is preserved.
42 ///
43 /// # Arguments
44 ///
45 /// * `recovery` - A function that takes the error and returns a new result
46 ///
47 /// # Examples
48 ///
49 /// ```
50 /// use error_rail::traits::ErrorOps;
51 ///
52 /// let result: Result<i32, &str> = Err("failed");
53 /// let recovered = result.recover(|_| Ok(0));
54 /// assert_eq!(recovered, Ok(0));
55 /// ```
56 fn recover<F>(self, recovery: F) -> Self
57 where
58 F: FnOnce(E) -> Self,
59 Self: Sized;
60
61 /// Maps both success and error cases simultaneously.
62 ///
63 /// This is equivalent to calling `map` followed by `map_err`, but more efficient
64 /// as it only matches once.
65 ///
66 /// # Arguments
67 ///
68 /// * `success_f` - Function to transform the success value
69 /// * `error_f` - Function to transform the error value
70 ///
71 /// # Examples
72 ///
73 /// ```
74 /// use error_rail::traits::ErrorOps;
75 ///
76 /// let result: Result<i32, &str> = Ok(21);
77 /// let mapped = result.bimap_result(|x| x * 2, |e| e.to_uppercase());
78 /// assert_eq!(mapped, Ok(42));
79 /// ```
80 fn bimap_result<B, F, SuccessF, ErrorF>(
81 self,
82 success_f: SuccessF,
83 error_f: ErrorF,
84 ) -> Result<B, F>
85 where
86 SuccessF: FnOnce(Self::Success) -> B,
87 ErrorF: FnOnce(E) -> F,
88 Self: Sized;
89}
90
91impl<T, E: Clone> ErrorOps<E> for Result<T, E> {
92 #[inline]
93 fn recover<F>(self, recovery: F) -> Self
94 where
95 F: FnOnce(E) -> Self,
96 {
97 match self {
98 Ok(value) => Ok(value),
99 Err(error) => recovery(error),
100 }
101 }
102
103 #[inline]
104 fn bimap_result<B, F, SuccessF, ErrorF>(
105 self,
106 success_f: SuccessF,
107 error_f: ErrorF,
108 ) -> Result<B, F>
109 where
110 SuccessF: FnOnce(T) -> B,
111 ErrorF: FnOnce(E) -> F,
112 {
113 match self {
114 Ok(value) => Ok(success_f(value)),
115 Err(error) => Err(error_f(error)),
116 }
117 }
118}