use std::{
fmt::{ self, Debug, Display, Formatter },
sync::{ Arc, Mutex }
};
#[derive(Debug)]
struct BacktraceRaw {
backtrace: backtrace::Backtrace,
readable: String
}
impl BacktraceRaw {
pub fn new_thin() -> Self {
Self { backtrace: backtrace::Backtrace::new_unresolved(), readable: String::new() }
}
pub fn ensure_resolved(&mut self) {
if self.readable.is_empty() {
self.backtrace.resolve();
self.readable = format!("{:?}", self.backtrace);
}
}
}
#[derive(Clone)]
pub struct Backtrace {
inner: Arc<Mutex<BacktraceRaw>>
}
impl Backtrace {
#[inline]
#[cfg(not(feature = "force_backtrace"))]
pub fn capture() -> Option<Self> {
let rust_backtrace = std::env::var("RUST_BACKTRACE").unwrap_or_default();
if !matches!(rust_backtrace.as_str(), "1" | "true" | "full") {
return None
}
let backtrace = BacktraceRaw::new_thin();
let this = Self { inner: Arc::new(Mutex::new(backtrace)) };
Some(this)
}
#[inline]
#[cfg(feature = "force_backtrace")]
pub fn capture() -> Option<Self> {
let backtrace = BacktraceRaw::new_thin();
let this = Self { inner: Arc::new(Mutex::new(backtrace)) };
Some(this)
}
}
impl Debug for Backtrace {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut inner = match self.inner.lock() {
Ok(inner) => inner,
Err(inner) => inner.into_inner()
};
inner.ensure_resolved();
f.debug_struct("Backtrace")
.field("inner", &inner)
.finish()
}
}
impl Display for Backtrace {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut inner = match self.inner.lock() {
Ok(inner) => inner,
Err(inner) => inner.into_inner()
};
inner.ensure_resolved();
write!(f, "{}", &inner.readable)
}
}