strerror 0.5.1

A string-based error type.
Documentation
//! A string-based error type.
//!
//! # Introduction
//!
//! This crate provides a string-based error type,
//! [`StrError`](StrError), that implements
//! [`std::error::Error`](std::error::Error). It is for simple use
//! cases where you wish to work with string errors and/or box
//! existing errors of any type, adding context to them.
//!
//! [`StrError`](StrError)s behave in many ways like
//! [`String`](String)s, except they may also contain another error
//! boxed inside them, known as the "source" or "cause". Since the
//! source can have a source itself, sources form chains of errors,
//! each error adding context to the preceeding one.
//!
//! When a [`StrError`](StrError) is returned from `main`, its
//! [`Debug`](Debug) implementation causes the output of a CLI
//! application to look like this
//!
//! ```text
//! Error: ...
//! Caused by: ...
//! Caused by: ...
//! ...
//! ```
//!
//! Each "Caused by:" line corresponds to a boxed error in the chain
//! of sources.
//!
//! # The prelude
//!
//! This crate has a prelude to bring in all the things you need at
//! once.
//!
//! ```
//! use strerror::prelude::*;
//! ```
//!
//! The examples below all assume use of the prelude.
//!
//! # Creating `StrError`s
//!
//! As with [`String`](String)s, there are quite a few ways to create
//! a [`StrError`](StrError). Some have an analagous
//! [`String`](String) equivalent, so are presented side-by-side with
//! them.
//!
//! ```
//! # use strerror::prelude::*;
//! // String                             // StrError
//! let str1 = "Error!".to_string();      let err1 = "Error!".into_error();
//! let str2 = String::from("Error!");    let err2 = StrError::from("Error!");
//! let str3: String = "Error!".into();   let err3: StrError = "Error!".into();
//! let str4 = format!("Error! #{}", 1);  let err4 = eformat!("Error! #{}", 1);
//! ```
//!
//! The above lines all create [`StrError`](StrError)s without a
//! "source" or "cause". To create a [`StrError`](StrError) with a
//! source there are two equivalent ways, shown below. In each case
//! the [`StrError`](StrError) takes ownership of the source which may
//! or may not be another [`StrError`](StrError).
//!
//! ```
//! # use strerror::prelude::*;
//! use std::io::Error as IoError;
//!
//! // Method 1: StrError::from_error
//! let source = IoError::from_raw_os_error(5);
//! let err1 = StrError::from_error(source, "I/O error occurred");
//!
//! ```
//!
//! ```
//! # use strerror::prelude::*;
//! use std::io::Error as IoError;
//!
//! // Method 2: chain
//! let source = IoError::from_raw_os_error(5);
//! let err2 = source.chain("I/O error occurred");
//! ```
//!
//! Chaining [`chain`](ErrorChainExt::chain) method calls together
//! creates an error chain.
//!
//! ```
//! # use strerror::prelude::*;
//! fn main() -> Result<(), StrError> {
//! # || -> Result<(), StrError> {
//!     let err = "Base error".into_error()
//!         .chain("Higher level error")
//!         .chain("Application error");
//!     Err(err)
//! # }().unwrap_err(); Ok(())
//! }
//! ```
//!
//! gives output
//!
//! ```text
//! Error: Application error
//! Caused by: Higher level error
//! Caused by: Base error
//! ```
//!
//! # Returning `Result`s
//!
//! While the [`chain`](ErrorChainExt::chain) method adds context to
//! error types directly, we can do a similar thing with the
//! [`Err`](Err) variant values in [`Result`](Result)s, with the
//! [`chain_err`](ResultChainErrExt::chain_err) method.
//!
//! ```
//! # use strerror::prelude::*;
//! use std::fs::File;
//!
//! fn main() -> Result<(), StrError> {
//! # || -> Result<(), StrError> {
//!     let file = "missing-file";
//!     let _ = File::open(file)                                // a Result
//!         .chain_err(|| format!("Failed to open {}", file))?; // main exits
//!     Ok(())
//! # }().unwrap_err(); Ok(())
//! }
//! ```
//!
//! gives output
//!
//! ```text
//! Error: Failed to open missing-file
//! Caused by: No such file or directory (os error 2)
//! ```
//!
//! The [`Result`](Result) is converted to the correct type by
//! [`chain_err`](ResultChainErrExt::chain_err) and context is applied
//! to the boxed error. Note that
//! [`chair_err`](ResultChainErrExt::chain_err) takes a closure, and
//! not a [`String`](String) directly. This is so the construction of
//! the [`String`](String) is not performed unless needed.
//!
//! If you are trying to return a [`Result`](Result) using the
//! `return` statement and the error in the return type of your
//! function requires a [`From`](From) conversion from a
//! [`StrError`](StrError), you might try this.
//!
//! ```
//! # use strerror::prelude::*;
//! use std::error::Error as StdError;
//!
//! fn main() -> Result<(), Box<dyn StdError>> {
//! # || -> Result<(), Box<dyn std::error::Error>> {
//!     return Err("an error".into_error().into());
//! # }().unwrap_err(); Ok(())
//! }
//! ```
//!
//! However, the [`into_err`](StringIntoErrExt::into_err) method is
//! easier since it will call [`into`](Into::into) and create the
//! [`Err`](Err) variant in one step.
//!
//!
//! ```
//! # use strerror::prelude::*;
//! use std::error::Error as StdError;
//!
//! fn main() -> Result<(), Box<dyn StdError>> {
//! # || -> Result<(), Box<dyn std::error::Error>> {
//!     return "an error".into_error().into_err();
//! # }().unwrap_err(); Ok(())
//! }
//! ```
//!
//! or even simpler
//!
//! ```
//! # use strerror::prelude::*;
//! use std::error::Error as StdError;
//!
//! fn main() -> Result<(), Box<dyn StdError>> {
//! # || -> Result<(), Box<dyn std::error::Error>> {
//!     return "an error".into_err();
//! # }().unwrap_err(); Ok(())
//! }
//! ```
//!
//! # Converting `Option`s
//!
//! Sometimes [`None`](None) represents an application error and it is
//! desirable to convert an [`Option<T>`](Option) into a
//! `Result<T, StrError>`. There is no special method needed in this
//! case. You can use [`ok_or_else`](Option::ok_or_else) with a
//! [`StrError`](StrError).
//!
//! ```
//! # use strerror::prelude::*;
//! use std::env;
//!
//! fn main() -> Result<(), StrError> {
//! # || -> Result<(), StrError> {
//!     let _ = env::var_os("MISSING_VAR")                         // an Option
//!         .ok_or_else(|| "MISSING_VAR not found".into_error())?; // exits
//!     Ok(())
//! # }().unwrap_err(); Ok(())
//! }
//! ```
//!
//! gives output
//!
//! ```text
//! Error: MISSING_VAR not found
//! ```
//!
//! If your containing function returns a `Result<T, StrError>`, like
//! in the case above, it is sufficient and simpler to pass a
//! [`&str`](str) to [`ok_or`](Option::ok_or) or a closure returning a
//! [`String`](String) to [`ok_or_else`](Option::ok_or_else). This
//! produces a `Result<T, &str>` or `Result<T, String>`, but when used
//! with the `?` operator it is converted to a
//! `Result<T, StrError>`. This works because [`StrError`](StrError)
//! implements `From<&str>` and `From<String>`.
//!
//! ```
//! # use strerror::prelude::*;
//! use std::env;
//!
//! fn main() -> Result<(), StrError> {
//! # || -> Result<(), StrError> {
//!     let _ = env::var_os("MISSING_VAR")    // an Option
//!         .ok_or("MISSING_VAR not found")?; // main exits
//!     Ok(())
//! # }().unwrap_err(); Ok(())
//! }
//! ```
//!
//! gives output
//!
//! ```text
//! Error: MISSING_VAR not found
//! ```
//!
//! # `From` conversions to `StrError`
//!
//! [`From`](From) conversions are implemented for most of the
//! standard library error types, so you can return a
//! [`Result`](Result) containing one directly from a function
//! expecting a `Result<T, StrError>`, using the `?` operator.
//!
//! ```
//! # use strerror::prelude::*;
//! use std::fs::File;
//!
//! fn main() -> Result<(), StrError> {
//! # || -> Result<(), StrError> {
//!     let file = "missing-file";
//!     let _ = File::open(file)?; // main exits
//!     Ok(())
//! # }().unwrap_err(); Ok(())
//! }
//! ```
//!
//! gives output
//!
//! ```text
//! Error: std::io::Error
//! Caused by: No such file or directory (os error 2)
//! ```
//!
//! [`From`](From) conversions are also implemented for [`&str`](str)
//! and [`String`](String), as mentioned in the previous section.
//!
//! However, for other error types, if you wish to use the `?`
//! operator you will first need to call the
//! [`chain_err`](ResultChainErrExt::chain_err) method to convert the
//! [`Result<T, E>`](Result) into a `Result<T, StrError>`. Of course
//! you may choose to use a `Box<dyn std::error::Error>` instead of a
//! [`StrError`](StrError) in the the return type of your function, in
//! which case `?` will work for all error types.
//!
//! # `Deref`
//!
//! [`StrError`](StrError)s deref to a [`String`](String), so you can
//! use many of the usual [`String`](String) methods.
//!
//! ```
//! # use strerror::prelude::*;
//! fn main() -> Result<(), StrError> {
//! # || -> Result<(), StrError> {
//!     let mut err = "This is".into_error();
//!     *err += " an error";
//!     err.push_str(" message");
//!     Err(err)
//! # }().unwrap_err(); Ok(())
//! }
//! ```
//!
//! gives output
//!
//! ```text
//! Error: This is an error message
//! ```
//!
//! # Iterating through the source chain
//!
//! A reference to a [`StrError`](StrError) can be iterated over to
//! examine its chain of boxed sources.
//!
//! ```
//! # use strerror::prelude::*;
//! use std::io::Error as IoError;
//!
//! fn main() -> Result<(), StrError> {
//!     let err = IoError::from_raw_os_error(5)
//!         .chain("Failure reading disk")
//!         .chain("Application error");
//!     for e in &err {
//!         println!(
//!             "Error: {:31} Is StrError?: {}",
//!             e,
//!             e.downcast_ref::<StrError>().is_some()
//!         );
//!     }
//!     Ok(())
//! }
//! ```
//!
//! gives output
//!
//! ```text
//! Error: Application error               Is StrError?: true
//! Error: Failure reading disk            Is StrError?: true
//! Error: Input/output error (os error 5) Is StrError?: false
//! ```

use std::borrow::Cow;
use std::error::Error;
use std::fmt::Error as FmtError;
use std::fmt::{Debug, Display, Formatter};
use std::ops::{Deref, DerefMut};

/// Reexports of [`eformat`](eformat),
/// [`ErrorChainExt`](ErrorChainExt),
/// [`ResultChainErrExt`](ResultChainErrExt), [`StrError`](StrError),
/// [`StringIntoErrExt`](StringIntoErrExt) and
/// [`StringIntoErrorExt`](StringIntoErrorExt)
pub mod prelude {
    pub use super::{
        eformat, ErrorChainExt, ResultChainErrExt, StrError, StringIntoErrExt,
        StringIntoErrorExt,
    };
}

/// A string-based error type implementing
/// [`std::error::Error`](std::error::Error).
///
/// [`From`](From) conversions to [`StrError`](StrError) are
/// implemented for most standard library error types, and for
/// [`String`](String)s and [`&str`](str)s. [`Deref`](Deref) converts
/// to a [`String`](String).
///
/// See crate level documentation for usage examples.
pub struct StrError(Box<StrErrorInner>);

struct StrErrorInner {
    context: String,
    source: Option<Box<dyn Error + Send + Sync>>,
}

impl StrError {
    /// Convert an error of any type to a [`StrError`](StrError),
    /// adding context and boxing it.
    pub fn from_error<S, E>(err: E, context: S) -> Self
    where
        S: Into<String>,
        E: Into<Box<dyn Error + Send + Sync>>,
    {
        StrError(Box::new(StrErrorInner {
            context: context.into(),
            source: Some(err.into()),
        }))
    }

    /// Create an iterator over a [`StrError`](StrError) and it's
    /// sources. When using the iterator, the first item retrieved is
    /// a reference to the [`StrError`](StrError) itself (as a trait
    /// object). This is then followed by the chain of sources. You
    /// can also use [`&StrError`](StrError)'s implementation of
    /// [`IntoIterator`](IntoIterator) to obtain an iterator.
    pub fn iter(&self) -> Iter<'_> {
        Iter { next: Some(self) }
    }

    /// Convert a [`StrError`](StrError) to an [`Err`](Err) variant
    /// [`Result`](Result), with [`From`](From) conversion from the
    /// [`StrError`](StrError).
    pub fn into_err<T, E>(self) -> Result<T, E>
    where
        E: From<StrError>,
    {
        Err(self.into())
    }

    /// Destroy the [`StrError`](StrError), returning its raw
    /// parts. These are its context string and its source error (if
    /// it has one).
    pub fn into_raw_parts(
        self,
    ) -> (String, Option<Box<dyn Error + Send + Sync>>) {
        (self.0.context, self.0.source)
    }
}

impl Error for StrError {
    /// Return the lower-level source of this [`StrError`](StrError),
    /// if any.
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.0.source.as_ref().map(|e| &**e as &(dyn Error + 'static))
    }
}

impl Display for StrError {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
        Display::fmt(&**self, f)
    }
}

impl Debug for StrError {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
        write!(f, "{}", self)?;
        for e in self.iter().skip(1) {
            write!(f, "\nCaused by: {}", e)?;
        }
        Ok(())
    }
}

/// An iterator producing a reference to a [`StrError`](StrError) (as
/// a trait object), followed by its chain of sources.
pub struct Iter<'a> {
    next: Option<&'a (dyn Error + 'static)>,
}

impl<'a> Iterator for Iter<'a> {
    type Item = &'a (dyn Error + 'static);

    fn next(&mut self) -> Option<Self::Item> {
        match &self.next {
            None => None,
            Some(e) => {
                let next = self.next;
                self.next = e.source();
                next
            }
        }
    }
}

impl<'a> IntoIterator for &'a StrError {
    type Item = &'a (dyn Error + 'static);
    type IntoIter = Iter<'a>;
    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

/// Trait providing [`chain`](ErrorChainExt::chain) for converting an
/// error of any type to a [`StrError`](StrError).
///
/// See crate level documentation for usage examples.
pub trait ErrorChainExt {
    /// Convert an error of any type to a [`StrError`](StrError), adding
    /// context.
    fn chain<S>(self, context: S) -> StrError
    where
        S: Into<String>;
}

impl<E> ErrorChainExt for E
where
    E: Into<Box<dyn Error + Send + Sync>>,
{
    fn chain<S>(self, context: S) -> StrError
    where
        S: Into<String>,
    {
        StrError::from_error(self.into(), context.into())
    }
}

/// Trait providing [`chain_err`](ResultChainErrExt::chain_err) for
/// mapping the [`Err`](Err) variant value within a [`Result`](Result)
/// to a [`StrError`](StrError).
///
/// See crate level documentation for usage examples.
pub trait ResultChainErrExt<T> {
    /// Map the [`Err`](Err) variant value within a [`Result`](Result)
    /// to a [`StrError`](StrError), adding context.
    fn chain_err<F, S>(self, context_fn: F) -> Result<T, StrError>
    where
        F: FnOnce() -> S,
        S: Into<String>;
}

impl<T, E> ResultChainErrExt<T> for Result<T, E>
where
    E: Into<Box<dyn Error + Send + Sync>>,
{
    fn chain_err<F, S>(self, context_fn: F) -> Result<T, StrError>
    where
        F: FnOnce() -> S,
        S: Into<String>,
    {
        self.map_err(|e| StrError::from_error(e.into(), context_fn().into()))
    }
}

/// Trait providing [`into_error`](StringIntoErrorExt::into_error) for
/// converting a [`String`](String) or [`&str`](str) to a
/// [`StrError`](StrError).
///
/// See crate level documentation for usage examples.
pub trait StringIntoErrorExt {
    /// Convert a [`String`](String) or [`&str`](str) to a
    /// [`StrError`](StrError).
    fn into_error(self) -> StrError;
}

impl<S> StringIntoErrorExt for S
where
    S: Into<String>,
{
    fn into_error(self) -> StrError {
        StrError::from(self.into())
    }
}

/// Trait providing [`into_err`](StringIntoErrExt::into_err) for
/// converting a [`String`](String) or [`&str`](str) to an
/// [`Err`](Err) variant [`Result`](Result).
///
/// Conversion is performed from the intermediate
/// [`StrError`](StrError) using [`into`](Into::into).
///
/// See crate level documentation for usage examples.
pub trait StringIntoErrExt {
    /// Convert a [`String`](String) or [`&str`](str) to an
    /// [`Err`](Err) variant [`Result`](Result).
    fn into_err<T, E>(self) -> Result<T, E>
    where
        E: From<StrError>;
}

impl<S> StringIntoErrExt for S
where
    S: Into<String>,
{
    fn into_err<T, E>(self) -> Result<T, E>
    where
        E: From<StrError>,
    {
        StrError::from(self.into()).into_err()
    }
}

/// A macro for creating a [`StrError`](StrError) using interpolation
/// of runtime expressions (like [`format!`](format!)).
///
/// [`eformat!`](eformat!) is an extremely simple macro meant to save
/// some keystrokes when creating [`StrError`](StrError)s. The name
/// was chosen to mirror that of [`eprint!`](eprint!) in the standard
/// library, which is an "error" version of [`print!`](print!).
///
/// Call [`eformat!`](eformat!) as you would call
/// [`format!`](format!), but you will get a [`StrError`](StrError)
/// instead of a [`String`](String).
#[macro_export]
macro_rules! eformat {
    ($($arg:tt)*) => {
        StrError::from(format!($($arg)*))
    }
}

// Deref conversion for StrError.
impl Deref for StrError {
    type Target = String;

    fn deref(&self) -> &String {
        &self.0.context
    }
}
impl DerefMut for StrError {
    fn deref_mut(&mut self) -> &mut String {
        &mut self.0.context
    }
}

// From conversions for Strings,  &strs etc
impl From<Box<str>> for StrError {
    fn from(s: Box<str>) -> Self {
        From::from(s.into_string())
    }
}
impl From<Cow<'_, str>> for StrError {
    fn from(s: Cow<'_, str>) -> Self {
        From::from(s.into_owned())
    }
}
impl From<&str> for StrError {
    fn from(s: &str) -> Self {
        From::from(s.to_owned())
    }
}
impl From<String> for StrError {
    fn from(s: String) -> Self {
        StrError(Box::new(StrErrorInner { context: s, source: None }))
    }
}
impl From<&String> for StrError {
    fn from(s: &String) -> Self {
        From::from(s.clone())
    }
}

// From conversions for stdlib errors.
macro_rules! impl_from {
    ($from:ty) => {
        impl From<$from> for StrError {
            fn from(err: $from) -> Self {
                StrError::from_error(err, stringify!($from))
            }
        }
    };
    ($from:ty, $param:ident $(, $bound:ident)* ) => {
        impl<$param> From<$from> for StrError
        where
            $param: Send + Sync + 'static $(+ $bound)*,
        {
            fn from(err: $from) -> Self {
                StrError::from_error(err, stringify!($from))
            }
        }
    };
}

impl_from!(std::alloc::LayoutErr);
impl_from!(std::array::TryFromSliceError);
impl_from!(std::boxed::Box<T>, T, Error);
impl_from!(std::cell::BorrowError);
impl_from!(std::cell::BorrowMutError);
impl_from!(std::char::CharTryFromError);
impl_from!(std::char::DecodeUtf16Error);
impl_from!(std::char::ParseCharError);
impl_from!(std::env::JoinPathsError);
impl_from!(std::env::VarError);
impl_from!(std::ffi::FromBytesWithNulError);
impl_from!(std::ffi::IntoStringError);
impl_from!(std::ffi::NulError);
impl_from!(std::fmt::Error);
impl_from!(std::io::Error);
impl_from!(std::io::IntoInnerError<T>, T, Debug);
impl_from!(std::net::AddrParseError);
impl_from!(std::num::ParseFloatError);
impl_from!(std::num::ParseIntError);
impl_from!(std::num::TryFromIntError);
impl_from!(std::path::StripPrefixError);
impl_from!(std::str::ParseBoolError);
impl_from!(std::str::Utf8Error);
impl_from!(std::string::FromUtf16Error);
impl_from!(std::string::FromUtf8Error);
impl_from!(std::string::ParseError);
impl_from!(std::sync::PoisonError<T>, T);
impl_from!(std::sync::TryLockError<T>, T);
impl_from!(std::sync::mpsc::RecvError);
impl_from!(std::sync::mpsc::RecvTimeoutError);
impl_from!(std::sync::mpsc::SendError<T>, T);
impl_from!(std::sync::mpsc::TryRecvError);
impl_from!(std::sync::mpsc::TrySendError<T>, T);
impl_from!(std::time::SystemTimeError);