summaryrefslogtreecommitdiff
path: root/src/unexpected.rs
blob: 6e5548b203e144841465d754037275e41db464bc (plain)
use core::fmt::{self, Debug, Display};

#[cfg(not(feature = "std"))]
use alloc::boxed::Box;

#[cfg(feature = "std")]
use std::error::Error;

pub trait Errorable: Display + Debug {}
impl<T: Display + Debug> Errorable for T {}

#[derive(Debug)]
#[non_exhaustive]
enum ErrorTy {
	Message(Box<dyn Errorable + 'static>),
	#[cfg(feature = "std")]
	Error(Box<dyn Error + 'static>),
}

#[derive(Debug)]
pub struct UnexpectedError {
	internal: ErrorTy,
}

impl Display for UnexpectedError {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match &self.internal {
			ErrorTy::Message(m) => Display::fmt(&m, f),
			#[cfg(feature = "std")]
			ErrorTy::Error(e) => Display::fmt(&e, f),
		}
	}
}

#[cfg(feature = "std")]
impl Error for UnexpectedError {
	fn source(&self) -> Option<&(dyn Error + 'static)> {
		match &self.internal {
			ErrorTy::Message(_) => None,
			#[cfg(feature = "std")]
			ErrorTy::Error(e) => Some(&**e),
		}
	}
}

impl UnexpectedError {
	/// Create a new `UnexpectedError` from any [`Error`] type.
	///
	/// The error must be `'static` so that the `UnexpectedError` will be too.
	///
	/// # Examples
	///
	/// Basic usage:
	///
	/// ```
	/// use exun::*;
	///
	/// let err = UnexpectedError::new(core::fmt::Error);
	/// ```
	#[cfg(feature = "std")]
	pub fn new<E: Error + 'static>(e: E) -> Self {
		Self {
			internal: ErrorTy::Error(Box::new(e)),
		}
	}

	/// Create an error message from a printable error message.
	///
	/// If the argument implements [`Error`], prefer [`UnexpectedError::new`]
	/// instead, which preserves the source.
	///
	/// # Examples
	///
	/// Basic usage:
	///
	/// ```
	/// use exun::*;
	///
	/// let err = UnexpectedError::msg("failed");
	/// ```
	pub fn msg<E: Errorable + 'static>(e: E) -> Self {
		Self {
			internal: ErrorTy::Message(Box::new(e)),
		}
	}
}