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};
pub mod prelude {
pub use super::{
eformat, ChainErr, ChainError, IntoErr, IntoError, StrError,
};
}
pub struct StrError(Box<StrErrorInner>);
struct StrErrorInner {
context: String,
source: Option<Box<dyn Error + Send + Sync>>,
}
impl StrError {
pub fn iter(&self) -> Iter<'_> {
Iter { next: Some(self) }
}
pub fn into_raw_parts(
self,
) -> (String, Option<Box<dyn Error + Send + Sync>>) {
(self.0.context, self.0.source)
}
}
impl Error for StrError {
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(())
}
}
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()
}
}
pub trait ChainError {
fn chain_error<C>(self, context: C) -> StrError
where
C: IntoChained;
}
impl<E> ChainError for E
where
E: Into<Box<dyn Error + Send + Sync>>,
{
fn chain_error<C>(self, context: C) -> StrError
where
C: IntoChained,
{
context.into_chained(self)
}
}
pub trait ChainErr<T> {
fn chain_err<C>(self, context: C) -> Result<T, StrError>
where
C: IntoChained;
}
impl<T, E> ChainErr<T> for Result<T, E>
where
E: Into<Box<dyn Error + Send + Sync>>,
{
fn chain_err<C>(self, context: C) -> Result<T, StrError>
where
C: IntoChained,
{
self.map_err(|e| context.into_chained(e))
}
}
pub trait IntoError {
fn into_error(self) -> StrError;
}
impl<S> IntoError for S
where
S: Into<StrError>,
{
fn into_error(self) -> StrError {
self.into()
}
}
pub trait IntoErr {
fn into_err<T, E>(self) -> Result<T, E>
where
Self: Into<E>;
}
impl<C> IntoErr for C {
fn into_err<T, E>(self) -> Result<T, E>
where
Self: Into<E>,
{
Err(self.into())
}
}
#[macro_export]
macro_rules! eformat {
($($arg:tt)*) => {
StrError::from(format!($($arg)*))
}
}
pub trait IntoChained {
fn into_chained<S>(self, source: S) -> StrError
where
S: Into<Box<dyn Error + Send + Sync>>;
}
impl IntoChained for Box<str> {
fn into_chained<S>(self, source: S) -> StrError
where
S: Into<Box<dyn Error + Send + Sync>>,
{
self.into_string().into_chained(source)
}
}
impl IntoChained for Cow<'_, str> {
fn into_chained<S>(self, source: S) -> StrError
where
S: Into<Box<dyn Error + Send + Sync>>,
{
self.into_owned().into_chained(source)
}
}
impl IntoChained for &str {
fn into_chained<S>(self, source: S) -> StrError
where
S: Into<Box<dyn Error + Send + Sync>>,
{
self.to_owned().into_chained(source)
}
}
impl IntoChained for String {
fn into_chained<S>(self, source: S) -> StrError
where
S: Into<Box<dyn Error + Send + Sync>>,
{
StrError(Box::new(StrErrorInner {
context: self,
source: Some(source.into()),
}))
}
}
impl IntoChained for &String {
fn into_chained<S>(self, source: S) -> StrError
where
S: Into<Box<dyn Error + Send + Sync>>,
{
self.clone().into_chained(source)
}
}
impl<F, C> IntoChained for F
where
F: FnOnce() -> C,
C: IntoChained,
{
fn into_chained<S>(self, source: S) -> StrError
where
S: Into<Box<dyn Error + Send + Sync>>,
{
self().into_chained(source)
}
}
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
}
}
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())
}
}
macro_rules! impl_from {
($from:ty) => {
impl From<$from> for StrError {
fn from(err: $from) -> Self {
stringify!($from).into_chained(err)
}
}
};
($from:ty, $param:ident $(, $bound:ident)* ) => {
impl<$param> From<$from> for StrError
where
$param: Send + Sync + 'static $(+ $bound)*,
{
fn from(err: $from) -> Self {
stringify!($from).into_chained(err)
}
}
};
}
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);