exun/
exun.rs

1use core::fmt::{self, Debug, Display};
2
3#[cfg(feature = "std")]
4use std::error::Error;
5
6use crate::{RawUnexpected, UnexpectedError};
7
8pub use Exun::{Expected, Unexpected};
9
10/// `Exun` is a type that represents either the expected error type
11/// ([`Expected`]) or an unexpected type ([`Unexpected`]).
12///
13/// See the [crate documentation](crate) for details.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
15pub enum Exun<E, U> {
16	/// Contains the expected type
17	Expected(E),
18	/// Contains an unexpected type
19	Unexpected(U),
20}
21
22impl<E: Display, U: Display> Display for Exun<E, U> {
23	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24		match self {
25			Expected(e) => e.fmt(f),
26			Unexpected(u) => u.fmt(f),
27		}
28	}
29}
30
31#[cfg(feature = "std")]
32impl<E: Error + 'static, U: Error + 'static> Error for Exun<E, U> {
33	fn source(&self) -> Option<&(dyn Error + 'static)> {
34		match self {
35			Expected(ref e) => Some(e),
36			Unexpected(ref u) => Some(u),
37		}
38	}
39}
40
41#[cfg(feature = "std")]
42impl<E: Error + 'static> Error for Exun<E, RawUnexpected> {
43	fn source(&self) -> Option<&(dyn Error + 'static)> {
44		match self {
45			Expected(ref e) => Some(e),
46			Unexpected(ref u) => u.source(),
47		}
48	}
49}
50
51#[cfg(feature = "std")]
52impl<E: Error, U> From<E> for Exun<E, U> {
53	fn from(e: E) -> Self {
54		Expected(e)
55	}
56}
57
58impl<E> From<RawUnexpected> for Exun<E, RawUnexpected> {
59	fn from(ue: RawUnexpected) -> Self {
60		Unexpected(ue)
61	}
62}
63
64impl<E> From<RawUnexpected> for Exun<E, UnexpectedError> {
65	fn from(ue: RawUnexpected) -> Self {
66		Unexpected(ue.into())
67	}
68}
69
70impl<E, U> Exun<E, U> {
71	/// Converts from `Exun<E, U>` to [`Option<E>`].
72	///
73	/// Converts `self` into an [`Option<E>`], consuming `self`, and discarding
74	/// the unexpected value, if any.
75	///
76	/// # Examples
77	///
78	/// ```
79	/// use exun::*;
80	///
81	/// let x: Exun<i32, &str> = Expected(2);
82	/// assert_eq!(x.expected(), Some(2));
83	///
84	/// let x: Exun<i32, &str> = Unexpected("Nothing here");
85	/// assert_eq!(x.expected(), None);
86	/// ```
87	#[allow(clippy::missing_const_for_fn)]
88	pub fn expected(self) -> Option<E> {
89		match self {
90			Expected(e) => Some(e),
91			Unexpected(_) => None,
92		}
93	}
94
95	/// Converts from `Exun<E, U>` to [`Option<U>`].
96	///
97	/// Converts `self` into an [`Option<U>`], consuming `self`, and discarding
98	/// the expected value, if any.
99	///
100	/// # Examples
101	///
102	/// ```
103	/// use exun::*;
104	///
105	/// let x: Exun<i32, &str> = Expected(2);
106	/// assert_eq!(x.unexpected(), None);
107	///
108	/// let x: Exun<i32, &str> = Unexpected("Nothing here");
109	/// assert_eq!(x.unexpected(), Some("Nothing here"));
110	/// ```
111	#[allow(clippy::missing_const_for_fn)]
112	pub fn unexpected(self) -> Option<U> {
113		match self {
114			Expected(_) => None,
115			Unexpected(u) => Some(u),
116		}
117	}
118
119	/// Converts from `&mut Exun<E, U>` to `Exun<&mut E, &mut U>`.
120	///
121	/// # Examples
122	///
123	/// ```
124	/// use exun::*;
125	///
126	/// fn mutate(r: &mut Exun<i32, i32>) {
127	///     match r.as_mut() {
128	///         Expected(e) => *e = 42,
129	///         Unexpected(u) => *u = 0,
130	///     }
131	/// }
132	///
133	/// let mut x = Expected(2);
134	/// mutate(&mut x);
135	/// assert_eq!(x.unwrap(), 42);
136	///
137	/// let mut x = Unexpected(13);
138	/// mutate(&mut x);
139	/// assert_eq!(x.unwrap_unexpected(), 0);
140	/// ```
141	pub fn as_mut(&mut self) -> Exun<&mut E, &mut U> {
142		match self {
143			Expected(ref mut e) => Expected(e),
144			Unexpected(ref mut u) => Unexpected(u),
145		}
146	}
147
148	/// Maps a `Exun<E, U>` to `Exun<T, U>` by applying a function to a
149	/// contained [`Expected`] value, leaving an [`Unexpected`] value
150	/// untouched.
151	///
152	/// This function can be used to compose the results of two functions.
153	///
154	/// # Examples
155	///
156	/// ```
157	/// use exun::*;
158	///
159	/// let x: Exun<i32, &str> = Expected(2);
160	/// assert_eq!(x.map(|i| i * 10), Expected(20));
161	///
162	/// let x: Exun<i32, &str> = Unexpected("unexpected");
163	/// assert_eq!(x.map(|i| i * 10), Unexpected("unexpected"));
164	/// ```
165	pub fn map<T, F: FnOnce(E) -> T>(self, op: F) -> Exun<T, U> {
166		match self {
167			Expected(e) => Expected(op(e)),
168			Unexpected(u) => Unexpected(u),
169		}
170	}
171
172	/// Maps a `Exun<E, U>` to `Exun<E, T>` by applying a function to a
173	/// contained [`Unexpected`] value, leaving an [`Expected`] value
174	/// untouched.
175	///
176	/// This function can be used to pass through an expected result while
177	/// handling an error.
178	///
179	/// # Examples
180	///
181	/// ```
182	/// use exun::*;
183	///
184	/// fn stringify(x: u32) -> String { format!("error code: {x}") }
185	///
186	/// let x: Exun<u32, u32> = Expected(2);
187	/// assert_eq!(x.map_unexpected(stringify), Expected(2));
188	///
189	/// let x: Exun<u32, u32> = Unexpected(13);
190	/// assert_eq!(x.map_unexpected(stringify), Unexpected("error code: 13".to_string()));
191	/// ```
192	pub fn map_unexpected<T, F: FnOnce(U) -> T>(self, op: F) -> Exun<E, T> {
193		match self {
194			Expected(e) => Expected(e),
195			Unexpected(u) => Unexpected(op(u)),
196		}
197	}
198
199	/// Returns the [`Expected`] value, consuming the `self` value.
200	///
201	/// Because this function may panic, its use is generally discouraged.
202	/// Instead, prefer to use pattern matching and handle the [`Unexpected`]
203	/// case explicitly.
204	///
205	/// # Panics
206	///
207	/// Panics if the value is an [`Unexpected`] value, with a panic message
208	/// including the passed message, and the content of the [`Unexpected`]
209	/// value.
210	///
211	/// # Examples
212	///
213	/// ```should_panic
214	/// use exun::*;
215	///
216	/// let x: Exun<u32, &str> = Exun::Unexpected("error");
217	/// x.expect("Testing expect"); // panics with "testing expect: error"
218	/// ```
219	///
220	/// # Recommended Message Style
221	///
222	/// We recommend that `expect` messages are used to describe the reason you
223	/// *expect* the `Exun` should be `Expected`.
224	///
225	/// ```should_panic
226	/// let path = std::env::var("IMPORTANT_PATH")
227	///     .expect("env variable `IMPORTANT_PATH` should be set by test.sh");
228	/// ```
229	///
230	/// **Hint:** If you're having trouble remembering how to phrase expect
231	/// error messages, remember to focus on the word "should" as in "env
232	/// variable set by blah" or "the given binary should be available and
233	/// executable by the current user".
234	///
235	/// For more detail on expect message styles and the reasoning behind the
236	/// recommendation please refer to the section on
237	/// ["Common Message Styles"](https://doc.rust-lang.org/stable/std/error/index.html#common-message-styles)
238	/// in the [`std::error`](https://doc.rust-lang.org/stable/std/error/index.html)
239	/// module docs.
240	pub fn expect(self, msg: &str) -> E
241	where
242		U: Debug,
243	{
244		match self {
245			Self::Expected(e) => e,
246			Self::Unexpected(e) => panic!("{}: {:?}", msg, e),
247		}
248	}
249
250	/// Returns the contained [`Expected`] value, consuming the `self` value.
251	///
252	/// Because this function may panic, its use is generally discouraged.
253	/// Instead, prefer to use pattern matching and handle the [`Unexpected`]
254	/// case explicitly, or call [`unwrap_or`] or [`unwrap_or_else`].
255	///
256	/// # Panics
257	///
258	/// Panics if the value is [`Unexpected`], with an panic message provided
259	/// by the [`Unexpected`]'s value.
260	///
261	/// # Examples
262	///
263	/// ```
264	/// use exun::*;
265	///
266	/// let x: Exun<u32, &str> = Expected(2);
267	/// assert_eq!(x.unwrap(), 2);
268	/// ```
269	///
270	/// ```should_panic
271	/// use exun::*;
272	///
273	/// let x: Exun<u32, &str> = Unexpected("emergency failure");
274	/// x.unwrap(); // panics with `emergency failure`
275	/// ```
276	///
277	/// [`unwrap_or`]: Self::unwrap_or
278	/// [`unwrap_or_else`]: Self::unwrap_or_else
279	pub fn unwrap(self) -> E
280	where
281		U: Debug,
282	{
283		match self {
284			Expected(e) => e,
285			Unexpected(u) => panic!("called `Expect::unwrap` on an `Unexpected` value: {:?}", u),
286		}
287	}
288
289	/// Returns the contained [`Unexpected`] value, consuming the `self` value.
290	///
291	/// # Panics
292	///
293	/// Panics if the value is [`Expected`], with an panic message provided by
294	/// the [`Expected`]'s value.
295	///
296	/// # Examples
297	///
298	/// ```should_panic
299	/// use exun::*;
300	///
301	/// let x: Exun<u32, &str> = Expected(2);
302	/// x.unwrap_unexpected(); // panics with `2`
303	/// ```
304	///
305	/// ```
306	/// use exun::*;
307	///
308	/// let x: Exun<u32, &str> = Unexpected("emergency failure");
309	/// assert_eq!(x.unwrap_unexpected(), "emergency failure");
310	/// ```
311	pub fn unwrap_unexpected(self) -> U
312	where
313		E: Debug,
314	{
315		match self {
316			Expected(e) => panic!(
317				"called `Expect::unwrap_unexpected` on an `Expected` value: {:?}",
318				e
319			),
320			Unexpected(u) => u,
321		}
322	}
323
324	/// Returns the contained [`Expected`] value or a provided default.
325	///
326	/// Arguments passed to `unwrap_or` are eagerly evaluated; if you are
327	/// passing the result of a function call, it is recommended to use
328	/// [`unwrap_or_else`], which is lazily evaluated.
329	///
330	/// # Examples
331	///
332	/// ```
333	/// use exun::*;
334	///
335	/// let default = 2;
336	/// let x: Exun<u32, &str> = Expected(9);
337	/// assert_eq!(x.unwrap_or(default), 9);
338	///
339	/// let x: Exun<u32, &str> = Unexpected("error");
340	/// assert_eq!(x.unwrap_or(default), default);
341	/// ```
342	///
343	/// [`unwrap_or_else`]: Self::unwrap_or_else
344	pub fn unwrap_or(self, default: E) -> E {
345		match self {
346			Expected(e) => e,
347			Unexpected(_) => default,
348		}
349	}
350
351	/// Returns the [`Expected`] value or returns it from a closure.
352	///
353	/// # Examples
354	///
355	/// ```
356	/// use exun::*;
357	///
358	/// fn count(x: &str) -> usize { x.len() }
359	///
360	/// assert_eq!(Expected(2).unwrap_or_else(count), 2);
361	/// assert_eq!(Unexpected("foo").unwrap_or_else(count), 3);
362	/// ```
363	pub fn unwrap_or_else(self, op: impl FnOnce(U) -> E) -> E {
364		match self {
365			Expected(e) => e,
366			Unexpected(u) => op(u),
367		}
368	}
369}