aboutsummaryrefslogtreecommitdiff
path: root/crates/stdx
diff options
context:
space:
mode:
Diffstat (limited to 'crates/stdx')
-rw-r--r--crates/stdx/src/lib.rs1
-rw-r--r--crates/stdx/src/panic_context.rs49
2 files changed, 50 insertions, 0 deletions
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 273b0f55b..011935cad 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -5,6 +5,7 @@ use std::{
5}; 5};
6 6
7mod macros; 7mod macros;
8pub mod panic_context;
8 9
9#[inline(always)] 10#[inline(always)]
10pub fn is_ci() -> bool { 11pub fn is_ci() -> bool {
diff --git a/crates/stdx/src/panic_context.rs b/crates/stdx/src/panic_context.rs
new file mode 100644
index 000000000..fd232e0cc
--- /dev/null
+++ b/crates/stdx/src/panic_context.rs
@@ -0,0 +1,49 @@
1//! A micro-crate to enhance panic messages with context info.
2//!
3//! FIXME: upstream to https://github.com/kriomant/panic-context ?
4
5use std::{cell::RefCell, panic, sync::Once};
6
7pub fn enter(context: String) -> impl Drop {
8 static ONCE: Once = Once::new();
9 ONCE.call_once(PanicContext::init);
10
11 with_ctx(|ctx| ctx.push(context));
12 PanicContext { _priv: () }
13}
14
15#[must_use]
16struct PanicContext {
17 _priv: (),
18}
19
20impl PanicContext {
21 fn init() {
22 let default_hook = panic::take_hook();
23 let hook = move |panic_info: &panic::PanicInfo<'_>| {
24 with_ctx(|ctx| {
25 if !ctx.is_empty() {
26 eprintln!("Panic context:");
27 for frame in ctx.iter() {
28 eprintln!("> {}\n", frame)
29 }
30 }
31 default_hook(panic_info)
32 })
33 };
34 panic::set_hook(Box::new(hook))
35 }
36}
37
38impl Drop for PanicContext {
39 fn drop(&mut self) {
40 with_ctx(|ctx| assert!(ctx.pop().is_some()))
41 }
42}
43
44fn with_ctx(f: impl FnOnce(&mut Vec<String>)) {
45 thread_local! {
46 static CTX: RefCell<Vec<String>> = RefCell::new(Vec::new());
47 }
48 CTX.with(|ctx| f(&mut *ctx.borrow_mut()))
49}