miette/
panic.rs

1use backtrace::Backtrace;
2use thiserror::Error;
3
4use crate::{self as miette, Context, Diagnostic, Result};
5
6/// Tells miette to render panics using its rendering engine.
7pub 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                // 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}