use core::any::Any;
use core::fmt::{self, Display, Write};
use core::panic::Location;
use core::ptr::NonNull;
use heapless::String;
pub trait Context: Any + Display {}
impl<T> Context for T where T: Any + Display {}
struct Frame {
context: NonNull<dyn Context>,
location: LocString,
#[cfg_attr(not(feature = "std"), allow(dead_code))]
next: Option<NonNull<Frame>>,
}
type LocString = String<100>;
#[cfg(any(doc, target_arch = "riscv32"))]
static FRAMES: crate::ContractCell<Option<NonNull<Frame>>> =
crate::ContractCell::new(None);
#[cfg(not(any(doc, target_arch = "riscv32")))]
std::thread_local! {
static FRAMES: core::cell::Cell<Option<NonNull<Frame>>> =
const { core::cell::Cell::new(None) };
}
#[track_caller]
pub fn with_debug_context<F, T>(context: &(impl Context + 'static), f: F) -> T
where
F: FnOnce() -> T,
{
let location_str = write_location(Location::caller());
let frames = FRAMES.get();
let frame = Frame {
context: NonNull::from(context),
location: location_str,
next: frames,
};
FRAMES.set(Some(NonNull::from(&frame)));
let _guard = FramesGuard { prev: frames };
f()
}
fn write_location(loc: &Location) -> LocString {
let mut out = LocString::new();
let mut rem = out.capacity();
let mut line_col = String::<16>::new();
write!(line_col, ":{}:{}", loc.line(), loc.column()).unwrap();
rem -= line_col.len();
let file = loc.file();
if file.len() > rem {
let elipsis = "...";
out.write_str(elipsis).unwrap();
rem -= elipsis.len();
out.write_str(&file[file.len() - rem..]).unwrap();
} else {
out.write_str(file).unwrap();
}
out.write_str(line_col.as_str()).unwrap();
out
}
struct FramesGuard {
prev: Option<NonNull<Frame>>,
}
impl Drop for FramesGuard {
fn drop(&mut self) {
FRAMES.set(self.prev);
}
}
impl Display for Frame {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let context = unsafe { self.context.as_ref() };
write!(f, "{} at {}", context, self.location)
}
}
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) struct Backtrace {
top: Option<NonNull<Frame>>,
}
impl Display for Backtrace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut frames = self.top;
let mut depth = 1;
while let Some(frame) = frames {
let frame = unsafe { frame.as_ref() };
writeln!(f, " {}: {}", depth, frame)?;
frames = frame.next;
depth += 1;
}
Ok(())
}
}
#[cfg_attr(not(feature = "std"), allow(dead_code))]
impl Backtrace {
pub(crate) unsafe fn capture() -> Self {
Backtrace { top: FRAMES.get() }
}
}