From 53bd72abada001fa6323a66da5c6761f6dce5c13 Mon Sep 17 00:00:00 2001 From: Botahamec Date: Sat, 22 Oct 2022 13:23:59 -0400 Subject: Create the Expect type --- src/lib.rs | 315 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 src/lib.rs (limited to 'src/lib.rs') 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 { + /// Contains the expected error type + Expected(E), + /// Contains an unexpected error + Unexpected(U), +} + +impl Display for Expect { + 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 Error for Expect { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Expected(ref e) => Some(e), + Unexpected(ref u) => Some(u), + } + } +} + +impl From for Expect { + fn from(e: E) -> Self { + Expected(e) + } +} + +impl Expect { + /// Converts from `Expect` to [`Option`]. + /// + /// Converts `self` into an [`Option`], consuming `self`, and discarding + /// the unexpected value, if any. + /// + /// # Examples + /// Basic usage: + /// ``` + /// use exun::*; + /// + /// let x: Expect = Expected(2); + /// assert_eq!(x.expected(), Some(2)); + /// + /// let x: Expect = Unexpected("Nothing here"); + /// assert_eq!(x.expected(), None); + /// ``` + #[allow(clippy::missing_const_for_fn)] + pub fn expected(self) -> Option { + match self { + Expected(e) => Some(e), + Unexpected(_) => None, + } + } + + /// Converts from `Expect` to [`Option`]. + /// + /// Converts `self` into an [`Option`], consuming `self`, and discarding + /// the expected value, if any. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use exun::*; + /// + /// let x: Expect = Expected(2); + /// assert_eq!(x.unexpected(), None); + /// + /// let x: Expect = Unexpected("Nothing here"); + /// assert_eq!(x.unexpected(), Some("Nothing here")); + /// ``` + #[allow(clippy::missing_const_for_fn)] + pub fn unexpected(self) -> Option { + match self { + Expected(_) => None, + Unexpected(u) => Some(u), + } + } + + /// Converts from `&mut Expect` to `Expect<&mut E, &mut U>`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use exun::*; + /// + /// fn mutate(r: &mut Expect) { + /// 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` to `Expect` 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 = Expected(2); + /// assert_eq!(x.map(|i| i * 10), Expected(20)); + /// + /// let x: Expect = Unexpected("unexpected"); + /// assert_eq!(x.map(|i| i * 10), Unexpected("unexpected")); + /// ``` + pub fn map T>(self, op: F) -> Expect { + match self { + Expected(e) => Expected(op(e)), + Unexpected(u) => Unexpected(u), + } + } + + /// Maps a `Expect` to `Expect` 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 = Expected(2); + /// assert_eq!(x.map_unexpected(stringify), Expected(2)); + /// + /// let x: Expect = Unexpected(13); + /// assert_eq!(x.map_unexpected(stringify), Unexpected("error code: 13".to_string())); + /// ``` + pub fn map_unexpected T>(self, op: F) -> Expect { + 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 = Expected(2); + /// assert_eq!(x.unwrap(), 2); + /// ``` + /// + /// ```should_panic + /// use exun::*; + /// + /// let x: Expect = 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 = Expected(2); + /// x.unwrap_unexpected(); // panics wirh `2` + /// ``` + /// + /// ``` + /// use exun::*; + /// + /// let x: Expect = 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 = Expected(9); + /// assert_eq!(x.unwrap_or(default), 9); + /// + /// let x: Expect = 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 E>(self, op: F) -> E { + match self { + Expected(e) => e, + Unexpected(u) => op(u), + } + } +} -- cgit v1.2.3