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> 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}