use std::fmt::{Debug, Display, Formatter};
/// Represents an error.
#[derive(Debug)]
pub struct Error {
/// Detail information of the error.
detail: Detail,
}
/// The detail information of an error.
enum Detail {
/// An error that only contains the `ErrorKind`.
Simple(ErrorKind),
/// An error that contains the `ErrorKind` and extra information.
Custom(Box<Custom>),
}
/// Represents a custom error.
#[derive(Debug)]
pub struct Custom {
/// Type of error.
kind: ErrorKind,
/// Extra information about the error.
error: Box<dyn std::error::Error + Send + Sync>,
}
/// A list of general errors.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum ErrorKind {
/// The value overflow.
Overflow,
/// The value is zero.
Zero,
/// The value is `Not a Number`.
NAN,
/// The provided input is invalid.
InvalidInput,
/// The provided number of arguments is invalid.
InvalidArgumentCount,
/// Performed a division by zero.
DivisionByZero,
/// The value is negative.
NegativeValue,
/// The value is positive.
PositiveValue,
/// The expression is invalid.
InvalidExpression,
/// The expression is empty.
Empty,
/// An unknown error.
Unknown,
}
impl ErrorKind {
/// Gets a `&str` representation of the `ErrorKind`.
pub fn as_str(&self) -> &'static str {
match *self {
ErrorKind::Overflow => "Value has overflow",
ErrorKind::Zero => "Value is zero",
ErrorKind::NAN => "Value is 'not a number'",
ErrorKind::InvalidInput => "Invalid input",
ErrorKind::InvalidArgumentCount => "Invalid number of arguments",
ErrorKind::DivisionByZero => "Cannot divide by zero",
ErrorKind::NegativeValue => "Value is negative",
ErrorKind::PositiveValue => "Value is positive",
ErrorKind::Empty => "Empty input",
ErrorKind::InvalidExpression => "Invalid expression",
ErrorKind::Unknown => "Unknown error",
}
}
}
impl Eq for Error {}
impl PartialEq for Error {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.kind() == other.kind()
}
}
impl Debug for Detail {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match *self {
Detail::Simple(ref kind) => f.write_str(kind.as_str()),
Detail::Custom(ref custom) => Debug::fmt(custom, f),
}
}
}
impl Error {
/// Creates a new `Error` an `ErrorKind` and inner error.
///
/// # Example
/// ```
/// use prexel::error::{Error, ErrorKind};
///
/// let custom_error = Error::new(ErrorKind::Unknown, "my error");
/// assert_eq!(ErrorKind::Unknown, custom_error.kind());
/// assert_eq!("my error", custom_error.to_string());
/// ```
#[inline]
pub fn new<E>(kind: ErrorKind, error: E) -> Error
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Error {
detail: Detail::Custom(Box::from(Custom {
kind,
error: error.into(),
})),
}
}
/// Creates an error with the specified message and `ErrorKind::Other`.
///
/// # Example
/// ```
/// use prexel::error::Error;
/// let error = Error::other("custom error");
/// assert_eq!("custom error", error.to_string());
/// ```
#[inline]
pub fn other(msg: &str) -> Error {
Self::new(ErrorKind::Unknown, msg)
}
/// Gets the `ErrorKind` of this error.
///
/// # Example
/// ```
/// use prexel::error::{Error, ErrorKind};
/// let error = Error::from(ErrorKind::InvalidInput);
/// assert_eq!(ErrorKind::InvalidInput, error.kind());
/// ```
#[inline]
pub fn kind(&self) -> ErrorKind {
match self.detail {
Detail::Simple(ref kind) => *kind,
Detail::Custom(ref custom) => custom.kind,
}
}
///Consumes the `Error`, returning its inner error (if any).
///
/// # Example
///```
/// use prexel::error::Error;
/// use prexel::error::ErrorKind;
///
/// fn print_error(error: Error){
/// if let Some(inner_error) = error.into_inner(){
/// println!("Inner error: {}", inner_error)
/// }
/// else{
/// println!("No inner error");
/// }
/// }
///
/// fn main(){
/// // No inner error
/// print_error(Error::from(ErrorKind::InvalidInput));
/// // With inner error
/// print_error(Error::new(ErrorKind::Unknown, "custom error"))
/// }
/// ```
#[inline]
pub fn into_inner(self) -> Option<Box<dyn std::error::Error + Send + Sync>> {
match self.detail {
Detail::Simple(_) => None,
Detail::Custom(custom) => Some(custom.error),
}
}
/// Gets a reference to the inner error (if any).
///
/// # Example
/// ```
/// use prexel::error::{Error, ErrorKind};
/// let error = Error::new(ErrorKind::Overflow, "value has overflow");
/// let inner_error = error.get_ref().unwrap();
/// ```
#[inline]
#[allow(clippy::borrowed_box)]
pub fn get_ref(&self) -> Option<&Box<dyn std::error::Error + Send + Sync>> {
match self.detail {
Detail::Simple(_) => None,
Detail::Custom(ref custom) => Some(&custom.error),
}
}
/// Gets a mutable reference to the inner error (if any).
///
/// # Example
/// ```
/// use prexel::error::{Error, ErrorKind};
/// let mut error = Error::new(ErrorKind::Overflow, "value has overflow");
/// let inner_error = error.get_mut().unwrap();
/// ```
#[inline]
pub fn get_mut(&mut self) -> Option<&mut Box<dyn std::error::Error + Send + Sync + 'static>> {
match self.detail {
Detail::Simple(_) => None,
Detail::Custom(ref mut custom) => Some(&mut custom.error),
}
}
}
impl From<ErrorKind> for Error {
#[inline]
fn from(kind: ErrorKind) -> Self {
Error {
detail: Detail::Simple(kind),
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.detail {
Detail::Simple(ref kind) => f.write_str(kind.as_str()),
Detail::Custom(ref custom) => Display::fmt(custom.error.as_ref(), f),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self.detail {
Detail::Simple(_) => None,
Detail::Custom(ref custom) => custom.error.source(),
}
}
#[allow(deprecated)]
fn cause(&self) -> Option<&dyn std::error::Error> {
match self.detail {
Detail::Simple(_) => None,
Detail::Custom(ref custom) => custom.error.cause(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn custom_error_test() {
let error = Error::new(ErrorKind::Unknown, "Just a test");
if let Detail::Custom(e) = error.detail {
assert_eq!(ErrorKind::Unknown, e.kind);
assert_eq!("Just a test", e.error.to_string())
} else {
unreachable!()
}
}
}