cauz 0.0.4

Succinct error handling
Documentation
/*
	Code to simplify provision of error context.
*/

//|
//| 3rd-party imports
//|
#[allow(unused_imports)]
pub use anyhow::{Context, Result, anyhow, bail}; // Re-export to avoid using `anyhow` in other modules.
use std::fmt::Display;

//|
//| err!
//|
#[macro_export]
macro_rules! err {
	() => {
		anyhow!("{:?} ", (file!(), line!()))
	};

	//| Convert & expand an Error
	($err:ident) => {
		anyhow!("{:?} {}", (file!(), line!()), anyhow!($err).chain().map(|e| e.to_string()).collect::<String>())
	};

	($msg:literal) => {
		anyhow!("{:?} {}", (file!(), line!()), $msg)
	};

	($fmt:literal, $($args:tt)*) => {
		anyhow!("{:?} {}", (file!(), line!()), format!($fmt, $($args)*))
	};

	($err:ident, $($args:tt)*) => {
		anyhow!("{:?} {} {}", (file!(), line!()), format!($($args)*), anyhow!($err).chain().map(|e| e.to_string()).collect::<String>())
	};

	//| Convert anything into an Error
	//# This must be below the pattern of ($err:ident) to have a lower matching priority
	($err:expr) => {
		anyhow!("{:?}", $err)
	};
}
// pub(crate) use err; // To avoid using #[macro_use] in main.rs. Ref: https://stackoverflow.com/questions/26731243/how-do-i-use-a-macro-across-module-files

//|
//| Cauz
//|
pub trait Cauz<C, T> {
	fn cauz(self, cause: C) -> Result<T>; //| Lazily evaluated `cause`
}

//| Result
impl<C, T, E> Cauz<C, T> for core::result::Result<T, E>
where
	C: Display + Send + Sync + 'static,
	E: Into<anyhow::Error>,
{
	#[inline(always)]
	fn cauz(self, cause: C) -> Result<T> {
		(self.map_err(|e| e.into() as anyhow::Error) as anyhow::Result<_>).with_context(|| cause)
	}
}

//| Option: A context function to replace `.cauz(err!(...)` (not lazy) or `.ok_or_else(|| err!(...))` (too long) with `.cauz(err!(...))`.
impl<C, T> Cauz<C, T> for Option<T>
where
	C: Display + Send + Sync + 'static,
{
	#[inline(always)]
	fn cauz(self, cause: C) -> Result<T> {
		self.with_context(|| cause)
	}
}

//| bool: A context function to replace `ensure!($expr, err!(...))` with `$expr.cauz(err!(...))?`.
impl<C> Cauz<C, ()> for bool
where
	C: Display + Send + Sync + 'static,
{
	#[inline(always)]
	fn cauz(self, cause: C) -> Result<()> {
		self.then_some(()).with_context(|| cause)
	}
}

//|
//| Cauz2
//|
pub trait Cauz2<C, T, E> {
	fn cauz2(self, cause: C, op: impl Fn(String) -> E) -> Result<T, E>; //| Lazily evaluated `cause`
}

//| Result
impl<C, T, E, E0> Cauz2<C, T, E> for core::result::Result<T, E0>
where
	C: Display + Send + Sync + 'static,
	E0: Into<anyhow::Error>,
{
	#[inline(always)]
	fn cauz2(self, cause: C, op: impl Fn(String) -> E) -> Result<T, E> {
		(self.map_err(|e0| e0.into() as anyhow::Error) as anyhow::Result<_>)
			.with_context(|| cause)
			.map_err(|e0| op(e0.chain().map(|e0| e0.to_string()).collect::<String>()))
	}
}

//| Option: A context function to replace `.cauz(err!(...)` (not lazy) or `.ok_or_else(|| err!(...))` (too long) with `.cauz(err!(...))`.
impl<C, T, E> Cauz2<C, T, E> for Option<T>
where
	C: Display + Send + Sync + 'static,
{
	#[inline(always)]
	fn cauz2(self, cause: C, op: impl Fn(String) -> E) -> Result<T, E> {
		self.with_context(|| cause).map_err(|e0| op(e0.chain().map(|e0| e0.to_string()).collect::<String>()))
	}
}

//| bool: A context function to replace `ensure!($expr, err!(...))` with `$expr.cauz(err!(...))?`.
impl<C, E> Cauz2<C, (), E> for bool
where
	C: Display + Send + Sync + 'static,
{
	#[inline(always)]
	fn cauz2(self, cause: C, op: impl Fn(String) -> E) -> Result<(), E> {
		self.then_some(()).with_context(|| cause).map_err(|e0| op(e0.chain().map(|e0| e0.to_string()).collect::<String>()))
	}
}