exun/result.rs
1use core::fmt::Debug;
2
3#[cfg(feature = "std")]
4use std::error::Error;
5
6use crate::{unexpected::Errorable, Exun, RawUnexpected};
7
8mod sealed {
9 pub trait Sealed {}
10 impl<T, E> Sealed for Result<T, E> {}
11 impl<T> Sealed for Option<T> {}
12}
13
14use sealed::Sealed;
15
16/// Provides [`Result::unexpect`]
17///
18/// [`Result::unexpect`]: `ResultErrorExt::unexpect`
19#[cfg(feature = "std")]
20pub trait ResultErrorExt<T>: Sealed {
21 /// Converts [`Result<T, E>`] to [`Result<T, RawUnexpected>`].
22 ///
23 /// # Examples
24 ///
25 /// ```
26 /// use exun::*;
27 /// use core::fmt::Error;
28 ///
29 /// let res: Result<i32, Error> = Err(Error);
30 /// let res: Result<i32, RawUnexpected> = res.unexpect();
31 /// ```
32 ///
33 /// Use with the try operator
34 ///
35 /// ```
36 /// use exun::*;
37 /// use core::fmt::Error;
38 ///
39 /// fn foo() -> Result<i32, UnexpectedError> {
40 /// let res: Result<i32, Error> = Err(Error);
41 /// Ok(res.unexpect()?)
42 /// }
43 /// ```
44 ///
45 /// Use with the try operator and [`Exun`]
46 ///
47 /// ```
48 /// use exun::*;
49 /// use core::fmt::Error;
50 ///
51 /// fn foo() -> Result<i32, Exun<(), UnexpectedError>> {
52 /// let res: Result<i32, Error> = Err(Error);
53 /// Ok(res.unexpect()?)
54 /// }
55 /// ```
56 ///
57 /// [`Exun`]: `crate::Exun`
58 #[allow(clippy::missing_errors_doc)]
59 fn unexpect(self) -> Result<T, RawUnexpected>;
60}
61
62#[cfg(feature = "std")]
63impl<T, E: Error + Send + Sync + 'static> ResultErrorExt<T> for Result<T, E> {
64 fn unexpect(self) -> Result<T, RawUnexpected> {
65 self.map_err(RawUnexpected::new)
66 }
67}
68
69#[cfg(feature = "std")]
70impl<T> ResultErrorExt<T> for Result<T, RawUnexpected> {
71 fn unexpect(self) -> Self {
72 self
73 }
74}
75
76#[cfg(feature = "std")]
77impl<T> ResultErrorExt<T> for Option<T> {
78 fn unexpect(self) -> Result<T, RawUnexpected> {
79 self.ok_or_else(RawUnexpected::none)
80 }
81}
82
83/// Provides [`Result::unexpect_msg`]
84///
85/// [`Result::unexpect_msg`]: `ResultMsgExt::unexpect_msg`
86#[cfg(feature = "alloc")]
87pub trait ResultMsgExt<T>: Sealed {
88 /// Converts [`Result<T, E>`] to [`Result<T, RawUnexpected>`].
89 ///
90 /// This is provided for compatibility with `no_std`. If your type
91 /// implements [`Error`], then you should prefer that instead.
92 ///
93 /// # Examples
94 ///
95 /// ```
96 /// use exun::*;
97 ///
98 /// let res: Result<i32, &str> = Err("failure");
99 /// let res: Result<i32, RawUnexpected> = res.unexpect_msg();
100 /// ```
101 ///
102 /// Use with the try operator
103 ///
104 /// ```
105 /// use exun::*;
106 ///
107 /// fn foo() -> Result<i32, UnexpectedError> {
108 /// let res: Result<i32, &str> = Err("failure");
109 /// Ok(res.unexpect_msg()?)
110 /// }
111 /// ```
112 ///
113 /// Use with the try operator and [`Exun`]
114 ///
115 /// ```
116 /// use exun::*;
117 ///
118 /// fn foo() -> Result<i32, Exun<(), UnexpectedError>> {
119 /// let res: Result<i32, &str> = Err("failure");
120 /// Ok(res.unexpect_msg()?)
121 /// }
122 /// ```
123 ///
124 /// [`Exun`]: `crate::Exun`
125 #[allow(clippy::missing_errors_doc)]
126 fn unexpect_msg(self) -> Result<T, RawUnexpected>;
127}
128
129#[cfg(feature = "alloc")]
130impl<T, E: Errorable + 'static> ResultMsgExt<T> for Result<T, E> {
131 fn unexpect_msg(self) -> Result<T, RawUnexpected> {
132 self.map_err(RawUnexpected::msg)
133 }
134}
135
136/// Provides [`Result::unexpect_none`] and [`Option::unexpect_none`]
137///
138/// [`Result::unexpect_none`]: `ResultNoneExt::unexpect_none`
139/// [`Option::unexpect_none`]: `ResultNoneExt::unexpect_none`
140pub trait ResultNoneExt<T>: Sealed {
141 /// Converts [`Result<T, E>`] or [`Option<T>`] to
142 /// [`Result<T, RawUnexpected>`].
143 ///
144 /// # Examples
145 ///
146 /// ```
147 /// use exun::*;
148 /// use core::fmt::Error;
149 ///
150 /// let res: Result<i32, Error> = Err(Error);
151 /// let res: Result<i32, RawUnexpected> = res.unexpect_none();
152 /// ```
153 ///
154 /// Use with the try operator
155 ///
156 /// ```
157 /// use exun::*;
158 /// use core::fmt::Error;
159 ///
160 /// fn foo() -> Result<i32, UnexpectedError> {
161 /// let res: Result<i32, Error> = Err(Error);
162 /// Ok(res.unexpect_none()?)
163 /// }
164 /// ```
165 ///
166 /// Use with the try operator and [`Exun`]
167 ///
168 /// ```
169 /// use exun::*;
170 /// use core::fmt::Error;
171 ///
172 /// fn foo() -> Result<i32, Exun<(), UnexpectedError>> {
173 /// let res: Result<i32, Error> = Err(Error);
174 /// Ok(res.unexpect_none()?)
175 /// }
176 /// ```
177 ///
178 /// Use with [`Option`]
179 ///
180 /// ```
181 /// use exun::*;
182 ///
183 /// fn foo() -> Result<i32, UnexpectedError> {
184 /// let option: Option<i32> = None;
185 /// Ok(option.unexpect_none()?)
186 /// }
187 /// ```
188 ///
189 /// [`Exun`]: `crate::Exun`
190 #[allow(clippy::missing_errors_doc)]
191 fn unexpect_none(self) -> Result<T, RawUnexpected>;
192}
193
194impl<T, E> ResultNoneExt<T> for Result<T, E> {
195 fn unexpect_none(self) -> Result<T, RawUnexpected> {
196 self.map_or_else(|_| Err(RawUnexpected::none()), |val| Ok(val))
197 }
198}
199
200impl<T> ResultNoneExt<T> for Option<T> {
201 fn unexpect_none(self) -> Result<T, RawUnexpected> {
202 self.ok_or_else(RawUnexpected::none)
203 }
204}
205
206pub trait ResultExunExt<T, E, U>: Sealed {
207 /// Converts [`Result<T, Exun<E, U>>`] to [`Option<E>`].
208 ///
209 /// Converts self into an [`Option<E>`], consuming `self`, and discarding
210 /// success value and the unexpected error, if any.
211 ///
212 /// # Examples
213 ///
214 /// ```
215 /// use exun::{Expected, Exun, ResultExunExt};
216 ///
217 /// let x: Result<u32, Exun<&str, &str>> = Ok(2);
218 /// assert_eq!(x.expected_err(), None);
219 ///
220 /// let x: Result<u32, Exun<&str, &str>> = Err(Expected("expected"));
221 /// assert_eq!(x.expected_err(), Some("expected"));
222 /// ```
223 fn expected_err(self) -> Option<E>;
224
225 /// Converts [`Result<T, Exun<E, U>>`] to [`Option<U>`].
226 ///
227 /// Converts self into an [`Option<U>`], consuming `self`, and discarding
228 /// success value and the expected error, if any.
229 ///
230 /// # Examples
231 ///
232 /// ```
233 /// use exun::{Exun, ResultExunExt, Unexpected};
234 ///
235 /// let x: Result<u32, Exun<&str, &str>> = Ok(2);
236 /// assert_eq!(x.unexpected_err(), None);
237 ///
238 /// let x: Result<u32, Exun<&str, &str>> = Err(Unexpected("unexpected"));
239 /// assert_eq!(x.unexpected_err(), Some("unexpected"));
240 /// ```
241 fn unexpected_err(self) -> Option<U>;
242
243 /// Maps a [`Result<T, Exun<E, U>>`] to `Result<T, Exun<F, U>>` by applying
244 /// a function to a contained `Err(Expected)` value, leaving the `Ok` and
245 /// `Err(Unexpected)` values untouched.
246 ///
247 /// This function can be used to pass through a successful result while
248 /// handling an expected error.
249 ///
250 /// # Examples
251 ///
252 /// ```
253 /// use exun::{Exun, ResultExunExt, Expected};
254 ///
255 /// fn stringify(x: u32) -> String { format!("error code: {x}") }
256 ///
257 /// let x: Result<u32, Exun<u32, &str>> = Ok(2);
258 /// assert_eq!(x.map_expected_err(stringify), Ok(2));
259 ///
260 /// let x: Result<u32, Exun<u32, &str>> = Err(Expected(13));
261 /// assert_eq!(x.map_expected_err(stringify), Err(Expected("error code: 13".to_string())));
262 /// ```
263 fn map_expected_err<F>(self, op: impl FnOnce(E) -> F) -> Result<T, Exun<F, U>>;
264
265 /// Maps a [`Result<T, Exun<E, U>>`] to `Result<T, Exun<E, F>>` by applying
266 /// a function to a contained `Err(Unexpected)` value, leaving the `Ok` and
267 /// `Err(Expected)` values untouched.
268 ///
269 /// This function can be used to pass through a successful result while
270 /// handling an unexpected error.
271 ///
272 /// # Examples
273 ///
274 /// ```
275 /// use exun::{Exun, ResultExunExt, Unexpected};
276 ///
277 /// fn stringify(x: &str) -> String { format!("error: {x}") }
278 ///
279 /// let x: Result<u32, Exun<u32, &str>> = Ok(2);
280 /// assert_eq!(x.map_unexpected_err(stringify), Ok(2));
281 ///
282 /// let x: Result<u32, Exun<u32, &str>> = Err(Unexpected("hi"));
283 /// assert_eq!(x.map_unexpected_err(stringify), Err(Unexpected("error: hi".to_string())));
284 /// ```
285 fn map_unexpected_err<F>(self, op: impl FnOnce(U) -> F) -> Result<T, Exun<E, F>>;
286
287 /// Converts [`Result<T, Exun<E, U>>`] to `Result<T, E>`, consuming the
288 /// self value.
289 ///
290 /// Because this function may panic, its use is generally discouraged.
291 /// Instead, prefer to use pattern matching and handle the [`Unexpected`]
292 /// case explicitly.
293 ///
294 /// # Panics
295 ///
296 /// Panics if the value is an [`Unexpected`], with a panic message provided
297 /// by the [`Unexpected`]'s value.
298 ///
299 /// # Examples
300 ///
301 /// ```
302 /// use exun::{Exun, ResultExunExt};
303 ///
304 /// let x: Result<u32, Exun<&str, &str>> = Ok(2);
305 /// assert_eq!(x.unwrap_result(), Ok(2));
306 /// ```
307 ///
308 /// [`Unexpected`]: crate::Unexpected
309 fn unwrap_result(self) -> Result<T, E>
310 where
311 U: Debug;
312
313 /// Returns the contained [`Expected`] value, consuming the `self` value.
314 ///
315 /// Because this function may panic, its use is generally discouraged.
316 /// Instead, prefer to use pattern matching and handle the [`Unexpected`]
317 /// case explicitly.
318 ///
319 /// # Panics
320 ///
321 /// Panics if the value is an [`Unexpected`], with a panic message provided
322 /// by the [`Unexpected`]'s value.
323 ///
324 /// # Examples
325 ///
326 /// ```
327 /// use exun::{Expected, Exun, ResultExunExt};
328 ///
329 /// let x: Result<u32, Exun<&str, &str>> = Err(Expected("failure"));
330 /// assert_eq!(x.unwrap_expected_err(), "failure");
331 /// ```
332 ///
333 /// [`Expected`]: crate::Expected
334 /// [`Unexpected`]: crate::Unexpected
335 fn unwrap_expected_err(self) -> E
336 where
337 T: Debug,
338 U: Debug;
339
340 /// Returns the contained [`Unexpected`] value, consuming the `self` value.
341 ///
342 /// Because this function may panic, its use is generally discouraged.
343 /// Instead, prefer to use pattern matching and handle the [`Expected`]
344 /// case explicitly.
345 ///
346 /// # Panics
347 ///
348 /// Panics if the value is an [`Expected`], with a panic message provided
349 /// by the [`Expected`]'s value.
350 ///
351 /// # Examples
352 ///
353 /// ```
354 /// use exun::{Exun, ResultExunExt, Unexpected};
355 ///
356 /// let x: Result<u32, Exun<&str, &str>> = Err(Unexpected("failure"));
357 /// assert_eq!(x.unwrap_unexpected_err(), "failure");
358 /// ```
359 ///
360 /// [`Expected`]: crate::Expected
361 /// [`Unexpected`]: crate::Unexpected
362 fn unwrap_unexpected_err(self) -> U
363 where
364 T: Debug,
365 E: Debug;
366}
367
368impl<T, E, U> ResultExunExt<T, E, U> for Result<T, Exun<E, U>> {
369 fn expected_err(self) -> Option<E> {
370 self.err()?.expected()
371 }
372
373 fn unexpected_err(self) -> Option<U> {
374 self.err()?.unexpected()
375 }
376
377 fn map_expected_err<F>(self, op: impl FnOnce(E) -> F) -> Result<T, Exun<F, U>> {
378 self.map_err(|e| e.map(op))
379 }
380
381 fn map_unexpected_err<F>(self, op: impl FnOnce(U) -> F) -> Result<T, Exun<E, F>> {
382 self.map_err(|e| e.map_unexpected(op))
383 }
384
385 fn unwrap_result(self) -> Result<T, E>
386 where
387 U: Debug,
388 {
389 match self {
390 Ok(value) => Ok(value),
391 Err(error) => Err(error.unwrap()),
392 }
393 }
394
395 fn unwrap_expected_err(self) -> E
396 where
397 T: Debug,
398 U: Debug,
399 {
400 self.unwrap_err().unwrap()
401 }
402
403 fn unwrap_unexpected_err(self) -> U
404 where
405 T: Debug,
406 E: Debug,
407 {
408 self.unwrap_err().unwrap_unexpected()
409 }
410}