1 use backtrace::Backtrace;
2 use thiserror::Error;
3 
4 use crate::{self as miette, Context, Diagnostic, Result};
5 
6 /// Tells miette to render panics using its rendering engine.
set_panic_hook()7 pub 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 = msg.clone();
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."))]
31 struct Panic(String);
32 
33 impl Panic {
backtrace() -> String34     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                 // Padding for next lines after frame's address
40                 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                         // Print symbols from this address,
56                         // if there are several addresses
57                         // we need to put it on next line
58                         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                         // See if there is debug information with file name and line
69                         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 }
87