ad_hoc_result/lib.rs
1//! # Ad Hoc Result
2//!
3//! A library providing an extension to Rust's standard `Result` type that
4//! allows for "ad hoc" values to be provided alongside errors.
5//!
6//! This is useful in scenarios where a computation may fail but can still
7//! recommend a reasonable value to use despite the failure.
8//! For example, this is needed when you can solve a linear system, but the accuracy is poor due to large condition numbers: you may want to return the computed solution as a recommendation, even though the operation is technically a failure.
9//! This is an interface to recommend a value anyway when computation fails.
10
11/// An enum representing either success (`Ok`), failure with a recommended value (`AdHoc`),
12/// or complete failure (`Err`).
13///
14/// # Type Parameters
15///
16/// * `T` - The type of the value in the success case or recommended value
17/// * `E` - The type of the error value
18///
19/// # Examples
20///
21/// ```
22/// use ad_hoc_result::AdHocResult;
23///
24/// // Simulate a function that returns AdHocResult
25/// fn divide(a: f64, b: f64) -> AdHocResult<f64, String> {
26/// if b == 0.0 {
27/// AdHocResult::AdHoc(f64::INFINITY, "Division by zero".to_string())
28/// } else {
29/// AdHocResult::Ok(a / b)
30/// }
31/// }
32///
33/// let result = divide(10.0, 2.0);
34/// assert_eq!(result.unwrap(), 5.0);
35///
36/// let ad_hoc_result = divide(10.0, 0.0);
37/// assert_eq!(ad_hoc_result.unwrap_adhoc(), f64::INFINITY);
38/// ```
39pub enum AdHocResult<T, E> {
40 /// Contains the success value
41 Ok(T),
42 /// Contains both a recommended value and an error explaining why the computation failed
43 AdHoc(T,E),
44 /// Contains only the error value
45 Err(E)
46}
47
48impl<T, E> AdHocResult<T, E> {
49 /// Unwraps a result, yielding the content of an `Ok`.
50 ///
51 /// # Panics
52 ///
53 /// Panics with the provided message if the value is an `AdHoc` or `Err`.
54 ///
55 /// # Examples
56 ///
57 /// ```
58 /// use ad_hoc_result::AdHocResult;
59 ///
60 /// let x: AdHocResult<u32, &str> = AdHocResult::Ok(2);
61 /// assert_eq!(x.expect("Testing expect"), 2);
62 /// ```
63 pub fn expect(self, message: &str) -> T {
64 match self {
65 AdHocResult::Ok(x) => x,
66 _ => panic!("{}", message)
67 }
68 }
69
70 /// Unwraps a result, yielding the content of an `Ok`.
71 ///
72 /// # Panics
73 ///
74 /// Panics with a generic "Unwrap fails" message if the value is an `AdHoc` or `Err`.
75 ///
76 /// # Examples
77 ///
78 /// ```
79 /// use ad_hoc_result::AdHocResult;
80 ///
81 /// let x: AdHocResult<u32, &str> = AdHocResult::Ok(2);
82 /// assert_eq!(x.unwrap(), 2);
83 /// ```
84 pub fn unwrap(self) -> T {
85 self.expect("Unwrap fails")
86 }
87
88 /// Unwraps a result, yielding the content of an `Ok` or `AdHoc`.
89 ///
90 /// # Panics
91 ///
92 /// Panics with the provided message if the value is an `Err`.
93 ///
94 /// # Examples
95 ///
96 /// ```
97 /// use ad_hoc_result::AdHocResult;
98 ///
99 /// let x: AdHocResult<u32, &str> = AdHocResult::AdHoc(2, "Not ideal");
100 /// assert_eq!(x.expect_adhoc("Testing expect_adhoc"), 2);
101 /// ```
102 pub fn expect_adhoc(self, message: &str) -> T {
103 match self {
104 AdHocResult::Ok(x) => x,
105 AdHocResult::AdHoc(x, _) => x,
106 _ => panic!("{}", message)
107 }
108 }
109
110 /// Unwraps a result, yielding the content of an `Ok` or `AdHoc`.
111 ///
112 /// # Panics
113 ///
114 /// Panics with a generic "Unwrap fails" message if the value is an `Err`.
115 ///
116 /// # Examples
117 ///
118 /// ```
119 /// use ad_hoc_result::AdHocResult;
120 ///
121 /// let x: AdHocResult<u32, &str> = AdHocResult::AdHoc(2, "Not ideal");
122 /// assert_eq!(x.unwrap_adhoc(), 2);
123 /// ```
124 pub fn unwrap_adhoc(self) -> T {
125 self.expect_adhoc("Unwrap fails")
126 }
127
128 /// Converts the `AdHocResult<T, E>` into a `Result<T, E>`.
129 ///
130 /// This conversion treats both `Err` and `AdHoc` variants as errors,
131 /// but for `AdHoc` it will discard the recommended value and only keep the error.
132 ///
133 /// # Examples
134 ///
135 /// ```
136 /// use ad_hoc_result::AdHocResult;
137 ///
138 /// let ok: AdHocResult<i32, &str> = AdHocResult::Ok(42);
139 /// assert_eq!(ok.to_result(), Ok(42));
140 ///
141 /// let adhoc: AdHocResult<i32, &str> = AdHocResult::AdHoc(42, "Not perfect");
142 /// assert_eq!(adhoc.to_result(), Err("Not perfect"));
143 ///
144 /// let err: AdHocResult<i32, &str> = AdHocResult::Err("Error");
145 /// assert_eq!(err.to_result(), Err("Error"));
146 /// ```
147 pub fn to_result(self) -> Result<T, E> {
148 match self {
149 AdHocResult::Ok(v) => Ok(v),
150 AdHocResult::AdHoc(_, e) => Err(e),
151 AdHocResult::Err(e) => Err(e),
152 }
153 }
154
155 /// Converts the `AdHocResult<T, E>` into a `Result<T, E>` treating `AdHoc` as success.
156 ///
157 /// This conversion treats only `Err` variant as an error, while both `Ok` and `AdHoc`
158 /// variants are treated as success, prioritizing the value but discarding any error
159 /// information from `AdHoc`.
160 ///
161 /// # Examples
162 ///
163 /// ```
164 /// use ad_hoc_result::AdHocResult;
165 ///
166 /// let ok: AdHocResult<i32, &str> = AdHocResult::Ok(42);
167 /// assert_eq!(ok.to_result_with_adhoc(), Ok(42));
168 ///
169 /// let adhoc: AdHocResult<i32, &str> = AdHocResult::AdHoc(42, "Not perfect");
170 /// assert_eq!(adhoc.to_result_with_adhoc(), Ok(42));
171 ///
172 /// let err: AdHocResult<i32, &str> = AdHocResult::Err("Error");
173 /// assert_eq!(err.to_result_with_adhoc(), Err("Error"));
174 /// ```
175 pub fn to_result_with_adhoc(self) -> Result<T, E> {
176 match self {
177 AdHocResult::Ok(v) => Ok(v),
178 AdHocResult::AdHoc(v, _) => Ok(v),
179 AdHocResult::Err(e) => Err(e),
180 }
181 }
182}
183
184impl<T, E> From<Result<T, E>> for AdHocResult<T, E> {
185 /// Converts a `Result<T, E>` into an `AdHocResult<T, E>`.
186 ///
187 /// This conversion maps `Ok` variants to `AdHocResult::Ok` and
188 /// `Err` variants to `AdHocResult::Err`.
189 ///
190 /// # Examples
191 ///
192 /// ```
193 /// use ad_hoc_result::AdHocResult;
194 ///
195 /// let ok_result: Result<i32, &str> = Ok(42);
196 /// let adhoc_ok: AdHocResult<i32, &str> = ok_result.into();
197 ///
198 /// match adhoc_ok {
199 /// AdHocResult::Ok(v) => assert_eq!(v, 42),
200 /// _ => panic!("Expected AdHocResult::Ok"),
201 /// }
202 ///
203 /// let err_result: Result<i32, &str> = Err("Error");
204 /// let adhoc_err: AdHocResult<i32, &str> = err_result.into();
205 ///
206 /// match adhoc_err {
207 /// AdHocResult::Err(e) => assert_eq!(e, "Error"),
208 /// _ => panic!("Expected AdHocResult::Err"),
209 /// }
210 /// ```
211 fn from(result: Result<T, E>) -> Self {
212 match result {
213 Ok(v) => AdHocResult::Ok(v),
214 Err(e) => AdHocResult::Err(e),
215 }
216 }
217}
218
219impl<T, E> From<AdHocResult<T, E>> for Result<T, E> {
220 /// Converts an `AdHocResult<T, E>` into a `Result<T, E>`.
221 ///
222 /// This conversion maps `AdHocResult::Ok` to `Ok` and both
223 /// `AdHocResult::AdHoc` and `AdHocResult::Err` to `Err`.
224 /// For `AdHoc`, the recommended value is discarded.
225 ///
226 /// # Examples
227 ///
228 /// ```
229 /// use ad_hoc_result::AdHocResult;
230 ///
231 /// let adhoc_ok: AdHocResult<i32, &str> = AdHocResult::Ok(42);
232 /// let ok_result: Result<i32, &str> = adhoc_ok.into();
233 /// assert_eq!(ok_result, Ok(42));
234 ///
235 /// let adhoc: AdHocResult<i32, &str> = AdHocResult::AdHoc(42, "Not perfect");
236 /// let adhoc_result: Result<i32, &str> = adhoc.into();
237 /// assert_eq!(adhoc_result, Err("Not perfect"));
238 ///
239 /// let adhoc_err: AdHocResult<i32, &str> = AdHocResult::Err("Error");
240 /// let err_result: Result<i32, &str> = adhoc_err.into();
241 /// assert_eq!(err_result, Err("Error"));
242 /// ```
243 fn from(adhoc: AdHocResult<T, E>) -> Self {
244 adhoc.to_result()
245 }
246}
247
248// Additional helper trait implementation
249impl<T, E> AdHocResult<T, E> {
250 /// Creates a new `AdHocResult` in the `Ok` variant.
251 ///
252 /// # Examples
253 ///
254 /// ```
255 /// use ad_hoc_result::AdHocResult;
256 ///
257 /// let x: AdHocResult<u32, &str> = AdHocResult::new_ok(42);
258 /// assert_eq!(x.unwrap(), 42);
259 /// ```
260 pub fn new_ok(value: T) -> Self {
261 AdHocResult::Ok(value)
262 }
263
264 /// Creates a new `AdHocResult` in the `AdHoc` variant with a recommended value and an error.
265 ///
266 /// # Examples
267 ///
268 /// ```
269 /// use ad_hoc_result::AdHocResult;
270 ///
271 /// let x: AdHocResult<u32, &str> = AdHocResult::new_adhoc(42, "Not ideal");
272 /// assert_eq!(x.unwrap_adhoc(), 42);
273 /// ```
274 pub fn new_adhoc(value: T, error: E) -> Self {
275 AdHocResult::AdHoc(value, error)
276 }
277
278 /// Creates a new `AdHocResult` in the `Err` variant.
279 ///
280 /// # Examples
281 ///
282 /// ```
283 /// use ad_hoc_result::AdHocResult;
284 ///
285 /// let x: AdHocResult<u32, &str> = AdHocResult::new_err("Error occurred");
286 /// assert!(matches!(x, AdHocResult::Err(_)));
287 /// ```
288 pub fn new_err(error: E) -> Self {
289 AdHocResult::Err(error)
290 }
291}