cauz 0.0.3

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> {
	#[allow(dead_code)] //todo: Why is this dead code when it's actually used?
	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)
	}
}

//|
//| CauzTonic
//|
#[cfg(feature = "cauz_tonic")]
pub use cauz_tonic::*;

#[cfg(feature = "cauz_tonic")]
mod cauz_tonic {
	use super::*;
	use std::fmt::Debug;
	use tonic::Status;

	//|
	//| CauzTonic
	//|
	pub trait CauzTonic<C, T> {
		#[allow(dead_code)] //todo: Why is this dead code when it's actually used?
		fn cauz2(self, cause: C) -> Result<T, Status>; //| Lazily evaluated `cause`
	}

	//| Result
	impl<C, T, E> CauzTonic<C, T> for core::result::Result<T, E>
	where
		C: Display + Send + Sync + 'static,
		E: Into<anyhow::Error>,
	{
		#[inline(always)]
		fn cauz2(self, cause: C) -> Result<T, Status> {
			(self.map_err(|e| (e.into() as anyhow::Error)) as anyhow::Result<_>)
				.with_context(|| cause)
				.map_err(|e| Status::internal(e.chain().map(|e| e.to_string()).collect::<String>()))
				.inspect_err(|e| log::error!("{}", e.message()))
		}
	}

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

	//| bool
	impl<C> CauzTonic<C, ()> for bool
	where
		C: Display + Send + Sync + 'static + Debug,
	{
		#[inline(always)]
		fn cauz2(self, cause: C) -> Result<(), Status> {
			self.then_some(()).ok_or_else(|| Status::internal(cause.to_string()))
		}
	}
}