diff options
| author | Botahamec <botahamec@outlook.com> | 2022-10-22 13:23:59 -0400 |
|---|---|---|
| committer | Botahamec <botahamec@outlook.com> | 2022-10-22 13:23:59 -0400 |
| commit | 53bd72abada001fa6323a66da5c6761f6dce5c13 (patch) | |
| tree | 8740e43777acf14d0c266922707ba6b4c96fba14 /src/lib.rs | |
Create the Expect type
Diffstat (limited to 'src/lib.rs')
| -rw-r--r-- | src/lib.rs | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..a4e7d48 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,315 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![warn(clippy::nursery)] +#![warn(clippy::pedantic)] + +use core::fmt::{self, Debug, Display}; + +#[cfg(feature = "std")] +use std::error::Error; + +pub use Expect::{Expected, Unexpected}; + +/// `Expect` is a type that represents either the expected error type +/// ([`Expected`]) or an unexpected error ([`Unexpected`]). +/// +/// See the [crate documentation](self) for details. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum Expect<E, U> { + /// Contains the expected error type + Expected(E), + /// Contains an unexpected error + Unexpected(U), +} + +impl<E: Display, U: Display> Display for Expect<E, U> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Expected(e) => e.fmt(f), + Unexpected(u) => u.fmt(f), + } + } +} + +#[cfg(feature = "std")] +impl<E: Error + 'static, U: Error + 'static> Error for Expect<E, U> { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Expected(ref e) => Some(e), + Unexpected(ref u) => Some(u), + } + } +} + +impl<E, U> From<E> for Expect<E, U> { + fn from(e: E) -> Self { + Expected(e) + } +} + +impl<E, U> Expect<E, U> { + /// Converts from `Expect<E, U>` to [`Option<E>`]. + /// + /// Converts `self` into an [`Option<E>`], consuming `self`, and discarding + /// the unexpected value, if any. + /// + /// # Examples + /// Basic usage: + /// ``` + /// use exun::*; + /// + /// let x: Expect<i32, &str> = Expected(2); + /// assert_eq!(x.expected(), Some(2)); + /// + /// let x: Expect<i32, &str> = Unexpected("Nothing here"); + /// assert_eq!(x.expected(), None); + /// ``` + #[allow(clippy::missing_const_for_fn)] + pub fn expected(self) -> Option<E> { + match self { + Expected(e) => Some(e), + Unexpected(_) => None, + } + } + + /// Converts from `Expect<E, U>` to [`Option<U>`]. + /// + /// Converts `self` into an [`Option<U>`], consuming `self`, and discarding + /// the expected value, if any. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use exun::*; + /// + /// let x: Expect<i32, &str> = Expected(2); + /// assert_eq!(x.unexpected(), None); + /// + /// let x: Expect<i32, &str> = Unexpected("Nothing here"); + /// assert_eq!(x.unexpected(), Some("Nothing here")); + /// ``` + #[allow(clippy::missing_const_for_fn)] + pub fn unexpected(self) -> Option<U> { + match self { + Expected(_) => None, + Unexpected(u) => Some(u), + } + } + + /// Converts from `&mut Expect<E, U>` to `Expect<&mut E, &mut U>`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use exun::*; + /// + /// fn mutate(r: &mut Expect<i32, i32>) { + /// match r.as_mut() { + /// Expected(e) => *e = 42, + /// Unexpected(u) => *u = 0, + /// } + /// } + /// + /// let mut x = Expected(2); + /// mutate(&mut x); + /// assert_eq!(x.unwrap(), 42); + /// + /// let mut x = Unexpected(13); + /// mutate(&mut x); + /// assert_eq!(x.unwrap_unexpected(), 0); + /// ``` + pub fn as_mut(&mut self) -> Expect<&mut E, &mut U> { + match self { + Expected(ref mut e) => Expected(e), + Unexpected(ref mut u) => Unexpected(u), + } + } + + /// Maps a `Expect<E, U>` to `Expect<T, U>` by applying a function to a + /// contained [`Expected`] value, leaving an [`Unexpected`] value + /// untouched. + /// + /// This function can be used to compose the results of two functions. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use exun::*; + /// + /// let x: Expect<i32, &str> = Expected(2); + /// assert_eq!(x.map(|i| i * 10), Expected(20)); + /// + /// let x: Expect<i32, &str> = Unexpected("unexpected"); + /// assert_eq!(x.map(|i| i * 10), Unexpected("unexpected")); + /// ``` + pub fn map<T, F: FnOnce(E) -> T>(self, op: F) -> Expect<T, U> { + match self { + Expected(e) => Expected(op(e)), + Unexpected(u) => Unexpected(u), + } + } + + /// Maps a `Expect<E, U>` to `Expect<E, T>` by applying a function to a + /// contained [`Unexpected`] value, leaving an [`Expected`] value + /// untouched. + /// + /// This function can be used to pass through an expected result while + /// handling an error. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use exun::*; + /// + /// fn stringify(x: u32) -> String { format!("error code: {x}") } + /// + /// let x: Expect<u32, u32> = Expected(2); + /// assert_eq!(x.map_unexpected(stringify), Expected(2)); + /// + /// let x: Expect<u32, u32> = Unexpected(13); + /// assert_eq!(x.map_unexpected(stringify), Unexpected("error code: 13".to_string())); + /// ``` + pub fn map_unexpected<T, F: FnOnce(U) -> T>(self, op: F) -> Expect<E, T> { + match self { + Expected(e) => Expected(e), + Unexpected(u) => Unexpected(op(u)), + } + } + + /// Returns the contained [`Expected`] value, consuming the `self` value. + /// + /// Because this function may panic, its use is generally discouraged. + /// Instead, prefer to use pattern matching and handle the [`Unexpected`] + /// case explicitly, or use [`unwrap_or`] or [`unwrap_or_else`]. + /// + /// [`unwrap_or`]: Expect::unwrap_or + /// [`unwrap_or_else`]: Expect::unwrap_or_else + /// + /// # Panics + /// + /// Panics if the value is [`Unexpected`], with an panic message provided + /// by the [`Unexpected`]'s value. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use exun::*; + /// + /// let x: Expect<u32, &str> = Expected(2); + /// assert_eq!(x.unwrap(), 2); + /// ``` + /// + /// ```should_panic + /// use exun::*; + /// + /// let x: Expect<u32, &str> = Unexpected("emergency failure"); + /// x.unwrap(); // panics with `emergency failure` + /// ``` + pub fn unwrap(self) -> E + where + U: Debug, + { + match self { + Expected(e) => e, + Unexpected(u) => panic!("called `Expect::unwrap` on an `Unexpected` value: {:?}", u), + } + } + + /// Returns the contained [`Unexpected`] value, consuming the `self` value. + /// + /// # Panics + /// + /// Panics if the value is [`Expected`], with an panic message provided by + /// the [`Expected`]'s value. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```should_panic + /// use exun::*; + /// + /// let x: Expect<u32, &str> = Expected(2); + /// x.unwrap_unexpected(); // panics wirh `2` + /// ``` + /// + /// ``` + /// use exun::*; + /// + /// let x: Expect<u32, &str> = Unexpected("emergency failure"); + /// assert_eq!(x.unwrap_unexpected(), "emergency failure"); + /// ``` + pub fn unwrap_unexpected(self) -> U + where + E: Debug, + { + match self { + Expected(e) => panic!( + "called `Expect::unwrap_unexpected` on an `Expected` value: {:?}", + e + ), + Unexpected(u) => u, + } + } + + /// Returns the contained [`Expected`] value, or a provided default. + /// + /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are + /// passing the result of a function call, it is recommended to use + /// [`unwrap_or_else`], which is lazily evaluated. + /// + /// [`unwrap_or_else`]: Expect::unwrap_or_else + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use exun::*; + /// + /// let default = 2; + /// let x: Expect<u32, &str> = Expected(9); + /// assert_eq!(x.unwrap_or(default), 9); + /// + /// let x: Expect<u32, &str> = Unexpected("unexpected"); + /// assert_eq!(x.unwrap_or(default), 2); + /// ``` + #[allow(clippy::missing_const_for_fn)] + pub fn unwrap_or(self, default: E) -> E { + match self { + Expected(e) => e, + Unexpected(_) => default, + } + } + + /// Returns the contained [`Expected`] value, or computes it from a + /// closure. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use exun::*; + /// + /// fn count(x: &str) -> usize { x.len() } + /// + /// assert_eq!(Expected(2).unwrap_or_else(count), 2); + /// assert_eq!(Unexpected("foo").unwrap_or_else(count), 3); + /// ``` + pub fn unwrap_or_else<F: FnOnce(U) -> E>(self, op: F) -> E { + match self { + Expected(e) => e, + Unexpected(u) => op(u), + } + } +} |
