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}