1use backtrace::Backtrace;
2use thiserror::Error;
3
4use crate::{self as miette, Context, Diagnostic, Result};
5
6pub fn set_panic_hook() {
8 std::panic::set_hook(Box::new(move |info| {
9 let mut message = "Something went wrong".to_string();
10 let payload = info.payload();
11 if let Some(msg) = payload.downcast_ref::<&str>() {
12 message = msg.to_string();
13 }
14 if let Some(msg) = payload.downcast_ref::<String>() {
15 message.clone_from(msg);
16 }
17 let mut report: Result<()> = Err(Panic(message).into());
18 if let Some(loc) = info.location() {
19 report = report
20 .with_context(|| format!("at {}:{}:{}", loc.file(), loc.line(), loc.column()));
21 }
22 if let Err(err) = report.with_context(|| "Main thread panicked.".to_string()) {
23 eprintln!("Error: {:?}", err);
24 }
25 }));
26}
27
28#[derive(Debug, Error, Diagnostic)]
29#[error("{0}{}", Panic::backtrace())]
30#[diagnostic(help("set the `RUST_BACKTRACE=1` environment variable to display a backtrace."))]
31struct Panic(String);
32
33impl Panic {
34 fn backtrace() -> String {
35 use std::fmt::Write;
36 if let Ok(var) = std::env::var("RUST_BACKTRACE") {
37 if !var.is_empty() && var != "0" {
38 const HEX_WIDTH: usize = std::mem::size_of::<usize>() + 2;
39 const NEXT_SYMBOL_PADDING: usize = HEX_WIDTH + 6;
41 let mut backtrace = String::new();
42 let trace = Backtrace::new();
43 let frames = backtrace_ext::short_frames_strict(&trace).enumerate();
44 for (idx, (frame, sub_frames)) in frames {
45 let ip = frame.ip();
46 let _ = write!(backtrace, "\n{:4}: {:2$?}", idx, ip, HEX_WIDTH);
47
48 let symbols = frame.symbols();
49 if symbols.is_empty() {
50 let _ = write!(backtrace, " - <unresolved>");
51 continue;
52 }
53
54 for (idx, symbol) in symbols[sub_frames].iter().enumerate() {
55 if idx != 0 {
59 let _ = write!(backtrace, "\n{:1$}", "", NEXT_SYMBOL_PADDING);
60 }
61
62 if let Some(name) = symbol.name() {
63 let _ = write!(backtrace, " - {}", name);
64 } else {
65 let _ = write!(backtrace, " - <unknown>");
66 }
67
68 if let (Some(file), Some(line)) = (symbol.filename(), symbol.lineno()) {
70 let _ = write!(
71 backtrace,
72 "\n{:3$}at {}:{}",
73 "",
74 file.display(),
75 line,
76 NEXT_SYMBOL_PADDING
77 );
78 }
79 }
80 }
81 return backtrace;
82 }
83 }
84 "".into()
85 }
86}