use core::fmt;
#[cfg(feature = "bt")]
#[derive(Debug)]
pub struct Bt(std::backtrace::Backtrace);
#[cfg(not(feature = "bt"))]
#[derive(Debug, Clone, Copy)]
pub struct Bt;
impl Bt {
#[inline]
pub fn capture() -> Self {
#[cfg(feature = "bt")]
{
Self(std::backtrace::Backtrace::capture())
}
#[cfg(not(feature = "bt"))]
{
Self
}
}
}
impl fmt::Display for Bt {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "bt")]
{
use std::backtrace::BacktraceStatus;
if matches!(self.0.status(), BacktraceStatus::Captured) {
write!(formatter, "\n{}", self.0)?;
}
Ok(())
}
#[cfg(not(feature = "bt"))]
{
let _ = formatter;
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn capture_does_not_panic() {
let _ = Bt::capture();
}
#[test]
fn display_is_format_safe() {
let _ = format!("{}", Bt::capture());
}
#[cfg(not(feature = "bt"))]
#[test]
fn bt_is_zero_sized_without_feature() {
assert_eq!(core::mem::size_of::<Bt>(), 0);
}
#[cfg(not(feature = "bt"))]
#[test]
fn display_is_empty_without_feature() {
assert_eq!(format!("{}", Bt::capture()), "");
}
#[cfg(feature = "bt")]
#[test]
fn bt_is_nonzero_sized_with_feature() {
assert!(core::mem::size_of::<Bt>() > 0);
}
#[cfg(feature = "bt")]
#[test]
fn display_renders_captured_backtrace() {
let bt = Bt(std::backtrace::Backtrace::force_capture());
let rendered = format!("{bt}");
assert!(
rendered.starts_with('\n'),
"captured backtrace must render with a leading newline so it sits \
beneath the error message; got {rendered:?}",
);
assert!(rendered.len() > 1, "captured backtrace must be non-empty");
}
}