use core::fmt::{self, Debug, Display};
#[cfg(feature = "std")]
use std::error::Error;
#[cfg(feature = "alloc")]
use crate::{RawUnexpected, UnexpectedError};
pub use Exun::{Expected, Unexpected};
/// `Expect` is a type that represents either the expected error type
/// ([`Expected`]) or an unexpected type ([`Unexpected`]).
///
/// See the [crate documentation](crate) for details.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Exun<E, U> {
/// Contains the expected type
Expected(E),
/// Contains an unexpected type
Unexpected(U),
}
impl<E: Display, U: Display> Display for Exun<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 Exun<E, U> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Expected(ref e) => Some(e),
Unexpected(ref u) => Some(u),
}
}
}
#[cfg(feature = "std")]
impl<E: Error + 'static> Error for Exun<E, RawUnexpected> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Expected(ref e) => Some(e),
Unexpected(ref u) => u.source(),
}
}
}
#[cfg(feature = "std")]
impl<E: Error, U> From<E> for Exun<E, U> {
fn from(e: E) -> Self {
Expected(e)
}
}
#[cfg(feature = "alloc")]
impl<E> From<RawUnexpected> for Exun<E, RawUnexpected> {
fn from(ue: RawUnexpected) -> Self {
Unexpected(ue)
}
}
#[cfg(feature = "alloc")]
impl<E> From<RawUnexpected> for Exun<E, UnexpectedError> {
fn from(ue: RawUnexpected) -> Self {
Unexpected(ue.into())
}
}
impl<E, U> Exun<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: Exun<i32, &str> = Expected(2);
/// assert_eq!(x.expected(), Some(2));
///
/// let x: Exun<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: Exun<i32, &str> = Expected(2);
/// assert_eq!(x.unexpected(), None);
///
/// let x: Exun<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 Exun<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) -> Exun<&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: Exun<i32, &str> = Expected(2);
/// assert_eq!(x.map(|i| i * 10), Expected(20));
///
/// let x: Exun<i32, &str> = Unexpected("unexpected");
/// assert_eq!(x.map(|i| i * 10), Unexpected("unexpected"));
/// ```
pub fn map<T, F: FnOnce(E) -> T>(self, op: F) -> Exun<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: Exun<u32, u32> = Expected(2);
/// assert_eq!(x.map_unexpected(stringify), Expected(2));
///
/// let x: Exun<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) -> Exun<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.
///
/// # Panics
///
/// Panics if the value is [`Unexpected`], with an panic message provided
/// by the [`Unexpected`]'s value.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use exun::*;
///
/// let x: Exun<u32, &str> = Expected(2);
/// assert_eq!(x.unwrap(), 2);
/// ```
///
/// ```should_panic
/// use exun::*;
///
/// let x: Exun<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: Exun<u32, &str> = Expected(2);
/// x.unwrap_unexpected(); // panics wirh `2`
/// ```
///
/// ```
/// use exun::*;
///
/// let x: Exun<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,
}
}
}