diff options
Diffstat (limited to 'crates/stdx/src/panic_context.rs')
-rw-r--r-- | crates/stdx/src/panic_context.rs | 49 |
1 files changed, 49 insertions, 0 deletions
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 | |||
5 | use std::{cell::RefCell, panic, sync::Once}; | ||
6 | |||
7 | pub 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] | ||
16 | struct PanicContext { | ||
17 | _priv: (), | ||
18 | } | ||
19 | |||
20 | impl 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 | |||
38 | impl Drop for PanicContext { | ||
39 | fn drop(&mut self) { | ||
40 | with_ctx(|ctx| assert!(ctx.pop().is_some())) | ||
41 | } | ||
42 | } | ||
43 | |||
44 | fn 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 | } | ||