#![deny(clippy::all)]
#![warn(clippy::nursery)]
#![warn(clippy::pedantic)]
#![allow(clippy::use_self)] #![allow(clippy::missing_errors_doc)] #![warn(unknown_lints)]
use std::{
error,
fmt::{self, Display, Formatter},
panic::Location,
string::ToString,
};
mod macros;
mod terminator;
pub use terminator::Terminator;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Error
{
pub ctx: String,
pub location: &'static Location<'static>,
pub cause: Option<Box<dyn error::Error + Send + 'static>>,
}
impl Error
{
#[allow(clippy::needless_pass_by_value)] #[track_caller]
pub fn new<S, E>(ctx: S, cause: E) -> Error
where
S: ToString,
E: error::Error + Send + 'static,
{
let ctx = ctx.to_string();
let location = Location::caller();
let cause: Option<Box<dyn error::Error + Send + 'static>> = Some(Box::new(cause));
Error { ctx, location, cause }
}
}
impl Display for Error
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result
{
write!(f, "{} ({})", self.ctx, self.location)
}
}
impl error::Error for Error
{
fn description(&self) -> &str { &self.ctx }
fn source(&self) -> Option<&(dyn error::Error + 'static)>
{
self.cause.as_ref().map(|c| &**c as _)
}
}
pub trait ResultExt<T>
{
#[track_caller]
fn context<S: ToString>(self, ctx: S) -> Result<T>;
#[track_caller]
fn with_context<S: ToString, F: FnOnce() -> S>(self, ctx_fn: F) -> Result<T>;
}
impl<T, E> ResultExt<T> for std::result::Result<T, E>
where
E: error::Error + Send + 'static,
{
fn context<S: ToString>(self, ctx: S) -> Result<T>
{
let location = Location::caller();
self.map_err(|e| Error { ctx: ctx.to_string(), location, cause: Some(Box::new(e)) })
}
fn with_context<S: ToString, F: FnOnce() -> S>(self, ctx_fn: F) -> Result<T>
{
let location = Location::caller();
self.map_err(|e| Error { ctx: ctx_fn().to_string(), location, cause: Some(Box::new(e)) })
}
}
pub trait ErrorExt: error::Error
{
fn iter_chain(&self) -> Causes;
fn iter_causes(&self) -> Causes { Causes { cause: self.iter_chain().nth(1) } }
fn find_root_cause(&self) -> &(dyn error::Error + 'static)
{
self.iter_chain().last().expect("source chain should at least contain original error")
}
}
impl<E: error::Error + 'static> ErrorExt for E
{
fn iter_chain(&self) -> Causes { Causes { cause: Some(self) } }
}
impl ErrorExt for dyn error::Error
{
fn iter_chain(&self) -> Causes { Causes { cause: Some(self) } }
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Causes<'a>
{
cause: Option<&'a (dyn error::Error + 'static)>,
}
impl<'a> Iterator for Causes<'a>
{
type Item = &'a (dyn error::Error + 'static);
fn next(&mut self) -> Option<Self::Item>
{
let cause = self.cause.take();
self.cause = cause.and_then(error::Error::source);
cause
}
}
#[inline]
#[allow(clippy::needless_pass_by_value)] #[track_caller]
pub fn err_msg<S: ToString>(ctx: S) -> Error
{
Error { ctx: ctx.to_string(), location: Location::caller(), cause: None }
}