Skip to main content

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}