use builder::*;
use context::*;
use core::fmt::{self, Debug, Display, Write};
use std::error::{self, Error};
use tags::Tags;
#[cfg(all(
feature = "debug_non_copyable_disabled",
feature = "debug_non_copyable_full"
))]
compile_error!(
"features `debug_non_copyable_disabled` and `debug_non_copyable_full` are mutually exclusive"
);
pub type Result<T, E = Oof> = std::result::Result<T, E>;
pub use ext::OofExt;
pub use oofs_derive::oofs;
#[macro_export]
macro_rules! oof {
($($arg:tt)*) => {
$crate::Oof::custom(format!($($arg)*))
};
}
#[macro_export]
macro_rules! ensure {
($cond:expr $(, $($rest:tt)*)?) => {
$crate::ensure!(@fmt $cond, (), $($($rest)*)?);
};
(@fmt $cond:expr, (), $({ $($rest:tt)* })?) => {
$crate::ensure!(@meta $cond, $crate::oof!("assertion failed: `{}`", stringify!($cond)), $($($rest)*)?);
};
(@fmt $cond:expr, ($($fmt:expr,)*), $({ $($rest:tt)* })?) => {
$crate::ensure!(@meta $cond, $crate::oof!($($fmt),*), $($($rest)*)?);
};
(@fmt $cond:expr, ($($fmt:expr,)*), $arg:expr $(, $($rest:tt)*)?) => {
$crate::ensure!(@fmt $cond, ($($fmt,)* $arg,), $($($rest)*)?);
};
(@meta $cond:expr, $ret:expr, tag: [$($tag:ty),* $(,)?] $(, $($rest:tt)*)?) => {
$crate::ensure!(@meta $cond, $ret $(.tag::<$tag>())*, $($($rest)*)?);
};
(@meta $cond:expr, $ret:expr, attach: [$($a:expr),* $(,)?] $(, $($rest:tt)*)?) => {
$crate::ensure!(@meta $cond, $ret $(.attach($a))*, $($($rest)*)?);
};
(@meta $cond:expr, $ret:expr, attach_lazy: [$($l:expr),* $(,)?] $(, $($rest:tt)*)?) => {
$crate::ensure!(@meta $cond, $ret $(.attach_lazy($l))*, $($($rest)*)?);
};
(@meta $cond:expr, $ret:expr, ) => {
if !$cond {
return $ret.into_res();
}
};
}
#[macro_export]
macro_rules! ensure_eq {
($l:expr, $r:expr $(, { $($rest:tt)* })?) => {
match (&$l, &$r) {
(left, right) => {
$crate::ensure!(*left == *right, "assertion failed: `(left == right)`", {
attach_lazy: [
|| format!(" left: {:?}", &*left),
|| format!("right: {:?}", &*right)
],
$($($rest)*)?
});
}
}
};
($l:expr, $r:expr $(, $($rest:tt)*)?) => {
match (&$l, &$r) {
(left, right) => {
$crate::ensure!(*left == *right $(, $($rest)*)?);
}
}
};
}
#[cfg_attr(feature = "location", track_caller)]
pub fn wrap_err(e: impl 'static + Send + Sync + Error) -> Oof {
Oof::builder().with_source(e).build()
}
pub struct Oof {
source: Option<Box<dyn 'static + Send + Sync + Error>>,
context: Box<Context>,
tags: Tags,
attachments: Vec<String>,
#[cfg(feature = "location")]
location: Location,
}
impl Display for Oof {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let context = self.context.as_ref();
write!(f, "{context}")?;
#[cfg(feature = "location")]
write!(f, " at `{}`", self.location)?;
if matches!(context, Context::Generated(_)) || !self.attachments.is_empty() {
writeln!(f)?;
}
if let Context::Generated(c) = context {
c.fmt_args(f)?;
}
if !self.attachments.is_empty() {
writeln!(f, "\nAttachments:")?;
for (i, a) in self.attachments.iter().enumerate() {
let mut indented = Indented {
inner: f,
number: Some(i),
started: false,
};
write!(indented, "{}", a)?;
writeln!(f)?;
}
}
Ok(())
}
}
impl Debug for Oof {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
#[cfg(not(feature = "location"))]
let debug = f
.debug_struct("Oof")
.field("context", &self.context)
.field("source", &self.source)
.field("tags", &self.tags)
.field("attachments", &self.attachments)
.finish();
#[cfg(feature = "location")]
let debug = f
.debug_struct("Oof")
.field("context", &self.context)
.field("source", &self.source)
.field("location", &self.location)
.field("tags", &self.tags)
.field("attachments", &self.attachments)
.finish();
return debug;
}
write!(f, "{self}")?;
if let Some(cause) = self.source() {
write!(f, "\nCaused by:")?;
let multiple = cause.source().is_some();
for (n, error) in chain::Chain::new(cause).enumerate() {
writeln!(f)?;
let mut indented = Indented {
inner: f,
number: if multiple { Some(n) } else { None },
started: false,
};
write!(indented, "{error}")?;
}
}
Ok(())
}
}
impl error::Error for Oof {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
if let Some(e) = &self.source {
Some(e.as_ref())
} else {
None
}
}
}
impl Oof {
#[cfg_attr(feature = "location", track_caller)]
pub fn custom(message: String) -> Oof {
Self::builder().with_custom(message).build()
}
pub fn into_res<T, E>(self) -> Result<T, E>
where
E: From<Self>,
{
Err(self.into())
}
#[cfg_attr(feature = "location", track_caller)]
fn builder() -> OofBuilder {
OofBuilder::new()
}
pub fn tagged<T: 'static>(&self) -> bool {
self.tags.tagged::<T>()
}
pub fn tagged_nested<T: 'static>(&self) -> bool {
if self.tagged::<T>() {
return true;
}
for cause in chain::Chain::new(self).skip(1) {
if let Some(e) = cause.downcast_ref::<Oof>() {
if e.tagged::<T>() {
return true;
}
}
}
false
}
pub fn tagged_nested_rev<T: 'static>(&self) -> bool {
for cause in chain::Chain::new(self).skip(1).rev() {
if let Some(e) = cause.downcast_ref::<Oof>() {
if e.tagged::<T>() {
return true;
}
}
}
if self.tagged::<T>() {
return true;
}
false
}
pub fn tag<T: 'static>(mut self) -> Self {
self.tags.tag::<T>();
self
}
pub fn attach<D: fmt::Debug>(mut self, debuggable: D) -> Self {
self.attachments.push(format!("{debuggable:?}"));
self
}
pub fn attach_lazy<D: ToString, F: FnOnce() -> D>(mut self, f: F) -> Self {
self.attachments.push(f().to_string());
self
}
}
mod builder;
mod chain;
mod context;
mod ext;
mod tags;
mod var_check;
pub mod __used_by_attribute {
pub use crate::{builder::*, context::*, tags::*, var_check::*};
pub const DEBUG_NON_COPYABLE: bool = cfg!(all(
not(feature = "debug_non_copyable_disabled"),
any(debug_assertions, feature = "debug_non_copyable_full")
));
}