use std::backtrace::Backtrace as StdBacktrace;
use std::error::Error as StdError;
pub trait ErrorExt: StdError {
fn message(&self) -> String;
fn backtrace(&self) -> &StdBacktrace;
fn has_backtrace(&self) -> bool {
self.backtrace().status() == std::backtrace::BacktraceStatus::Captured
}
fn find_source<T: StdError + 'static>(&self) -> Option<&T> {
self.find_source_with(|_| true)
}
fn find_source_with<T: StdError + 'static>(&self, search: impl Fn(&T) -> bool) -> Option<&T> {
let mut source = self.source();
while let Some(err) = source {
if let Some(target) = err.downcast_ref::<T>()
&& search(target)
{
return Some(target);
}
source = err.source();
}
None
}
}
#[cfg(test)]
mod tests {
use std::backtrace::BacktraceStatus;
use super::*;
use crate::backtrace::Backtrace;
#[ohno::error]
struct TestError;
#[cfg_attr(miri, ignore)] #[test]
fn force_backtrace_capture() {
let mut error = TestError::new();
error.0.data.backtrace = Backtrace::force_capture();
assert!(error.has_backtrace());
let backtrace = error.backtrace();
assert!(backtrace.status() == BacktraceStatus::Captured);
let display = format!("{error}");
assert!(display.starts_with("TestError\n\nBacktrace:\n"));
}
#[test]
fn no_backtrace_capture() {
let mut error = TestError::new();
error.0.data.backtrace = Backtrace::disabled();
assert!(!error.has_backtrace());
assert!(error.backtrace().status() == BacktraceStatus::Disabled);
let display = format!("{error}");
assert_eq!(display, "TestError");
}
}