human_errors/
result.rs

1use std::borrow::Cow;
2
3use super::*;
4
5/// Extension trait for `Result` to convert errors into user-friendly or
6/// system-friendly `Error` types.
7///
8/// # Examples
9/// ```
10/// use human_errors::ResultExt;
11///
12/// // Converts any error into a user-caused error with the provided advice.
13/// "0.not a number".parse::<i32>()
14///     .map_err_as_user(&["Please provide a valid integer input."]);
15///
16/// // Converts any error into a system-caused error with the provided advice.
17/// "0.not a number".parse::<i32>()
18///     .map_err_as_system(&["Please check your system configuration."]);
19///
20/// // Wraps any error into a user-caused error with a custom message and advice.
21/// "0.not a number".parse::<i32>()
22///     .wrap_err_as_user(
23///         "Failed to parse the provided input as an integer.",
24///         &["Please provide a valid integer input."],
25///     );
26///
27/// // Wraps any error into a system-caused error with a custom message and advice.
28/// "0.not a number".parse::<i32>()
29///     .wrap_err_as_system(
30///         "Failed to parse the provided input as an integer.",
31///         &["Please check your system configuration."],
32///     );
33/// ```
34pub trait ResultExt<T> {
35    /// Converts a `Result<T, E>` into a `Result<T, Error>`, wrapping any
36    /// error in a user-facing error with the provided advice.
37    ///
38    /// # Examples
39    /// ```
40    /// use human_errors::ResultExt;
41    ///
42    /// "0.not a number".parse::<i32>()
43    ///     .map_err_as_user(&["Please provide a valid integer input."]);
44    /// ```
45    fn map_err_as_user(self, advice: &'static [&'static str]) -> Result<T, Error>;
46
47    /// Converts a `Result<T, E>` into a `Result<T, Error>`, wrapping any
48    /// error in a user-facing error with the provided description and advice.
49    ///
50    /// # Examples
51    /// ```
52    /// use human_errors::ResultExt;
53    ///
54    /// "0.not a number".parse::<i32>()
55    ///     .wrap_err_as_user(
56    ///         "Failed to parse the provided input as an integer.",
57    ///         &["Please provide a valid integer input."],
58    ///     );
59    /// ```
60    fn wrap_err_as_user<S: Into<Cow<'static, str>> + 'static>(
61        self,
62        message: S,
63        advice: &'static [&'static str],
64    ) -> Result<T, Error>;
65
66    /// Converts a `Result<T, E>` into a `Result<T, Error>`, wrapping any
67    /// error in a system-facing error with the provided advice.
68    ///
69    /// # Examples
70    /// ```
71    /// use human_errors::ResultExt;
72    ///
73    /// "0.not a number".parse::<i32>()
74    ///     .map_err_as_system(&["Please report this issue to the dev team."]);
75    /// ```
76    fn map_err_as_system(self, advice: &'static [&'static str]) -> Result<T, Error>;
77
78    /// Converts a `Result<T, E>` into a `Result<T, Error>`, wrapping any
79    /// error in a system-facing error with the provided description and advice.
80    /// # Examples
81    /// ```
82    /// use human_errors::ResultExt;
83    ///
84    /// "0.not a number".parse::<i32>()
85    ///     .wrap_err_as_system(
86    ///         "Failed to parse the provided input as an integer.",
87    ///         &["Please report this issue to the dev team."],
88    ///     );
89    /// ```
90    fn wrap_err_as_system<S: Into<Cow<'static, str>> + 'static>(
91        self,
92        message: S,
93        advice: &'static [&'static str],
94    ) -> Result<T, Error>;
95}
96
97impl<T, E> ResultExt<T> for Result<T, E>
98where
99    E: Into<Box<dyn std::error::Error + Send + Sync>> + 'static,
100{
101    fn map_err_as_user(self, advice: &'static [&'static str]) -> Result<T, Error> {
102        self.map_err(|e| user(e, advice))
103    }
104
105    fn wrap_err_as_user<S: Into<Cow<'static, str>> + 'static>(
106        self,
107        message: S,
108        advice: &'static [&'static str],
109    ) -> Result<T, Error> {
110        self.map_err(|e| user(wrap_user(e, message, advice), advice))
111    }
112
113    fn map_err_as_system(self, advice: &'static [&'static str]) -> Result<T, Error> {
114        self.map_err(|e| system(e, advice))
115    }
116
117    fn wrap_err_as_system<S: Into<Cow<'static, str>> + 'static>(
118        self,
119        message: S,
120        advice: &'static [&'static str],
121    ) -> Result<T, Error> {
122        self.map_err(|e| system(wrap_system(e, message, advice), advice))
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn test_into_user_error() {
132        let result: Result<i32, std::io::Error> = Err(std::io::Error::other("underlying error"));
133
134        let user_error = result
135            .map_err_as_user(&["Please check your input and try again."])
136            .err()
137            .unwrap();
138
139        assert!(user_error.is(Kind::User));
140    }
141
142    #[test]
143    fn test_into_system_error() {
144        let result: Result<i32, std::io::Error> = Err(std::io::Error::other("underlying error"));
145
146        let system_error = result
147            .map_err_as_system(&["Please check your input and try again."])
148            .err()
149            .unwrap();
150
151        assert!(system_error.is(Kind::System));
152    }
153}