use std::{
fmt::{self, Debug},
panic::Location,
};
#[derive(Debug, thiserror::Error)]
pub enum ErrorKind {
#[error("UnitError")]
UnitError,
#[error("TimeoutError")]
TimeoutError,
#[error("StrError")]
StrError(&'static str),
#[error("StringError")]
StringError(String),
#[error("BoxedError")]
BoxedError(Box<dyn std::error::Error>),
#[error("TryFromIntError")]
TryFromIntError(std::num::TryFromIntError),
#[error("StdIoError")]
StdIoError(std::io::Error),
#[error("FromUtf8Error")]
FromUtf8Error(std::string::FromUtf8Error),
#[error("FromUtf16Error")]
FromUtf16Error(std::string::FromUtf16Error),
#[error("ParseIntError")]
ParseIntError(std::num::ParseIntError),
#[error("ParseFloatError")]
ParseFloatError(std::num::ParseFloatError),
#[cfg(feature = "tokio_rt_support")]
#[error("TokioJoinError")]
TokioJoinError(tokio::task::JoinError),
#[cfg(feature = "ron_support")]
#[error("RonError")]
RonError(ron::error::Error),
#[cfg(feature = "serde_json_support")]
#[error("SerdeJsonError")]
SerdeJsonError(serde_json::Error),
#[cfg(feature = "ctrlc_support")]
#[error("CtrlcError")]
CtrlcError(ctrlc::Error),
#[cfg(feature = "toml_support")]
#[error("TomlDeError")]
TomlDeError(toml::de::Error),
#[cfg(feature = "toml_support")]
#[error("TomlSerError")]
TomlSerError(toml::ser::Error),
#[cfg(feature = "serde_yaml_support")]
#[error("SerdeYamlError")]
SerdeYamlError(serde_yaml::Error),
#[cfg(feature = "reqwest_support")]
#[error("ReqwestError")]
ReqwestError(reqwest::Error),
#[cfg(feature = "hyper_support")]
#[error("HyperError")]
HyperError(hyper::Error),
}
pub struct ErrorInner {
pub stack: Vec<(ErrorKind, Option<&'static Location<'static>>)>,
}
pub struct Error(pub ErrorInner);
impl Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("Error {{ stack: [\n"))?;
for (i, (error, location)) in self.0.stack.iter().enumerate().rev() {
match error {
ErrorKind::UnitError => (),
ErrorKind::StrError(s) => {
if i == 0 {
f.write_fmt(format_args!("{s}\n"))?;
} else {
f.write_fmt(format_args!("{s} ->\n"))?;
}
}
ErrorKind::StringError(s) => {
if i == 0 {
f.write_fmt(format_args!("{s}\n"))?;
} else {
f.write_fmt(format_args!("{s} ->\n"))?;
}
}
_ => {
f.write_fmt(format_args!("{error:?},\n"))?;
}
}
if let Some(location) = location {
f.write_fmt(format_args!("{location:?},\n"))?;
}
}
f.write_fmt(format_args!("] }}"))
}
}
impl Error {
#[track_caller]
pub fn from_kind<K: Into<ErrorKind>>(kind: K) -> Self {
let l = Location::caller();
Self(ErrorInner {
stack: vec![(kind.into(), Some(l))],
})
}
#[track_caller]
pub fn timeout() -> Self {
Self::from_kind(ErrorKind::TimeoutError)
}
#[track_caller]
pub fn boxed(e: Box<dyn std::error::Error>) -> Self {
Self::from_kind(ErrorKind::BoxedError(e))
}
pub fn add_err_no_location<K: Into<ErrorKind>>(mut self, kind: K) -> Self {
self.0.stack.push((kind.into(), None));
self
}
#[track_caller]
pub fn add_err<K: Into<ErrorKind>>(mut self, kind: K) -> Self {
self.0.stack.push((kind.into(), Some(Location::caller())));
self
}
#[track_caller]
pub fn add_location(mut self) -> Self {
self.0
.stack
.push((ErrorKind::UnitError, Some(Location::caller())));
self
}
pub fn is_timeout(&self) -> bool {
for (error, _) in &self.0.stack {
if matches!(error, ErrorKind::TimeoutError) {
return true
}
}
false
}
pub fn chain_errors(mut self, mut other: Self) -> Self {
self.0.stack.append(&mut other.0.stack);
self
}
}
pub trait MapAddError {
type Output;
fn map_add_err<K: Into<ErrorKind>, F: FnOnce() -> K>(self, f: F) -> Self::Output;
}
impl<T> MapAddError for core::result::Result<T, Error> {
type Output = core::result::Result<T, Error>;
#[track_caller]
fn map_add_err<K: Into<ErrorKind>, F: FnOnce() -> K>(self, f: F) -> Self::Output {
match self {
Ok(o) => Ok(o),
Err(e) => Err(e.add_err(f())),
}
}
}
impl<T> MapAddError for Option<T> {
type Output = core::result::Result<T, Error>;
#[track_caller]
fn map_add_err<K: Into<ErrorKind>, F: FnOnce() -> K>(self, f: F) -> Self::Output {
match self {
Some(o) => Ok(o),
None => Err(Error::from_kind(f())),
}
}
}
impl<T, K0: Into<ErrorKind>> MapAddError for core::result::Result<T, K0> {
type Output = core::result::Result<T, Error>;
#[track_caller]
fn map_add_err<K1: Into<ErrorKind>, F: FnOnce() -> K1>(self, f: F) -> Self::Output {
match self {
Ok(o) => Ok(o),
Err(kind) => Err(Error::from_kind(kind).add_err_no_location(f())),
}
}
}
impl MapAddError for Error {
type Output = core::result::Result<(), Error>;
#[track_caller]
fn map_add_err<K: Into<ErrorKind>, F: FnOnce() -> K>(self, f: F) -> Self::Output {
Err(self.add_err(f()))
}
}
impl<K0: Into<ErrorKind>> MapAddError for K0 {
type Output = core::result::Result<(), Error>;
#[track_caller]
fn map_add_err<K1: Into<ErrorKind>, F: FnOnce() -> K1>(self, f: F) -> Self::Output {
Err(Error::from_kind(self).add_err(f()))
}
}
pub type Result<T> = std::result::Result<T, Error>;
macro_rules! unit_x {
($kind:ident $x:ty) => {
impl From<$x> for ErrorKind {
fn from(_e: $x) -> Self {
Self::$kind
}
}
impl From<$x> for Error {
#[track_caller]
fn from(e: $x) -> Self {
Self::from_kind(e)
}
}
};
}
macro_rules! x {
($kind:ident $x:ty) => {
impl From<$x> for ErrorKind {
fn from(e: $x) -> Self {
Self::$kind(e)
}
}
impl From<$x> for Error {
#[track_caller]
fn from(e: $x) -> Self {
Self::from_kind(e)
}
}
};
}
type X0 = ();
unit_x!(UnitError X0);
type X1 = &'static str;
x!(StrError X1);
type X2 = String;
x!(StringError X2);
type X3 = std::io::Error;
x!(StdIoError X3);
type X4 = std::string::FromUtf8Error;
x!(FromUtf8Error X4);
type X5 = std::string::FromUtf16Error;
x!(FromUtf16Error X5);
#[cfg(feature = "tokio_rt_support")]
type X6 = tokio::task::JoinError;
#[cfg(feature = "tokio_rt_support")]
x!(TokioJoinError X6);
#[cfg(feature = "serde_json_support")]
type X7 = serde_json::Error;
#[cfg(feature = "serde_json_support")]
x!(SerdeJsonError X7);
#[cfg(feature = "ron_support")]
type X8 = ron::error::Error;
#[cfg(feature = "ron_support")]
x!(RonError X8);
#[cfg(feature = "ctrlc_support")]
type X9 = ctrlc::Error;
#[cfg(feature = "ctrlc_support")]
x!(CtrlcError X9);
type X10 = std::num::ParseIntError;
x!(ParseIntError X10);
type X11 = std::num::ParseFloatError;
x!(ParseFloatError X11);
type X12 = std::num::TryFromIntError;
x!(TryFromIntError X12);
type X13 = Box<dyn std::error::Error>;
x!(BoxedError X13);
#[cfg(feature = "toml_support")]
type X14 = toml::de::Error;
#[cfg(feature = "toml_support")]
x!(TomlDeError X14);
#[cfg(feature = "toml_support")]
type X15 = toml::ser::Error;
#[cfg(feature = "toml_support")]
x!(TomlSerError X15);
#[cfg(feature = "serde_yaml_support")]
type X16 = serde_yaml::Error;
#[cfg(feature = "serde_yaml_support")]
x!(SerdeYamlError X16);
#[cfg(feature = "reqwest_support")]
type X17 = reqwest::Error;
#[cfg(feature = "reqwest_support")]
x!(ReqwestError X17);
#[cfg(feature = "hyper_support")]
type X18 = hyper::Error;
#[cfg(feature = "hyper_support")]
x!(HyperError X18);