#![doc(html_root_url="https://docs.rs/panic-context/0.1.0")]
#[macro_use] extern crate lazy_static;
use std::panic;
use std::collections::BTreeMap;
use std::cell::RefCell;
use std::sync::Mutex;
use std::io::Write;
lazy_static! {
static ref INITIALIZED: Mutex<bool> = Mutex::new(false);
}
struct Values {
next_id: usize,
values: BTreeMap<usize, String>,
}
thread_local! {
static VALUES: RefCell<Values> = RefCell::new(Values {
next_id: 0,
values: BTreeMap::new(),
});
}
fn init() {
let previous_hook = panic::take_hook();
panic::set_hook(Box::new(move |info| {
VALUES.with(|traces| {
let traces = traces.borrow();
let stderr = std::io::stderr();
let mut handle = stderr.lock();
let _ = handle.write(b"Panic context:\n");
for (_, value) in traces.values.iter() {
let _ = handle.write(value.as_bytes()).unwrap();
let _ = handle.write(b"\n").unwrap();
}
});
previous_hook(info);
}));
}
fn ensure_initialized() {
let mut initialized = INITIALIZED.lock().unwrap();
if !*initialized {
init();
*initialized = true;
}
}
fn add_entry(value: Option<String>) -> usize {
VALUES.with(move |values| {
let mut values = values.borrow_mut();
let id = values.next_id;
values.next_id += 1;
if let Some(v) = value {
values.values.insert(id, v);
}
id
})
}
fn update_entry(id: usize, value: String) {
VALUES.with(|values| {
let mut values = values.borrow_mut();
values.values.insert(id, value);
})
}
#[must_use]
pub struct UpdatablePanicContext {
id: usize,
prefix: &'static str,
}
impl UpdatablePanicContext {
pub fn new(prefix: &'static str) -> Self {
ensure_initialized();
let id = add_entry(None);
UpdatablePanicContext { id, prefix }
}
pub fn update<T: Into<String>>(&self, value: T) {
let mut buf = self.prefix.to_string();
buf += &value.into();
update_entry(self.id, buf);
}
}
#[must_use]
pub struct PanicContext {
id: usize,
}
impl PanicContext {
pub fn new<T: Into<String>>(msg: T) -> Self {
ensure_initialized();
let id = VALUES.with(|values| {
let mut values = values.borrow_mut();
let id = values.next_id;
values.next_id += 1;
values.values.insert(id, msg.into());
id
});
PanicContext { id }
}
}
impl Drop for PanicContext {
fn drop(&mut self) {
VALUES.with(|values| {
let mut values = values.borrow_mut();
values.values.remove(&self.id)
});
}
}
pub fn panic_context(prefix: &'static str) -> UpdatablePanicContext {
UpdatablePanicContext::new(prefix)
}
#[macro_export]
macro_rules! panic_context {
($($arg:tt)+) => (
let _panic_context = $crate::PanicContext::new(format!($($arg)+));
)
}
#[macro_export]
macro_rules! debug_panic_context {
($($arg:tt)+) => (
#[cfg(or(debug-assertions, keep-debug-context))]
let _panic_context = $crate::PanicContext::new_with_msg(format!($($arg)+));
)
}