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}