Skip to main content

human_panic/
panic.rs

1use std::io::Result as IoResult;
2use std::panic::PanicHookInfo;
3use std::path::{Path, PathBuf};
4
5use crate::Metadata;
6use crate::report::Report;
7
8#[doc(hidden)]
9pub fn setup_panic(meta: impl Fn() -> Metadata) {
10    #![allow(deprecated)]
11
12    #[allow(unused_imports)]
13    use std::panic;
14
15    match PanicStyle::default() {
16        PanicStyle::Debug => {}
17        PanicStyle::Human => {
18            let meta = meta();
19
20            panic::set_hook(Box::new(move |info: &PanicHookInfo<'_>| {
21                let report = Report::with_panic(&meta, info);
22                let file_path = if is_ci() { None } else { report.persist().ok() };
23                if file_path.is_none() {
24                    use std::io::Write as _;
25                    let stderr = std::io::stderr();
26                    let mut stderr = stderr.lock();
27
28                    let _ = writeln!(
29                        stderr,
30                        "{}",
31                        report
32                            .serialize()
33                            .expect("only doing toml compatible types")
34                    );
35                }
36                print_msg(file_path.as_deref(), &meta)
37                    .expect("human-panic: printing error message to console failed");
38            }));
39        }
40    }
41}
42
43/// Returns whether we are running in a CI environment.
44fn is_ci() -> bool {
45    std::env::var_os("CI").is_some()
46}
47
48/// Style of panic to be used
49#[non_exhaustive]
50#[derive(Copy, Clone, PartialEq, Eq)]
51pub enum PanicStyle {
52    /// Normal panic
53    Debug,
54    /// Human-formatted panic
55    Human,
56}
57
58impl Default for PanicStyle {
59    fn default() -> Self {
60        if cfg!(debug_assertions) {
61            PanicStyle::Debug
62        } else {
63            match ::std::env::var("RUST_BACKTRACE") {
64                Ok(_) => PanicStyle::Debug,
65                Err(_) => PanicStyle::Human,
66            }
67        }
68    }
69}
70
71/// Utility function that prints a message to our human users
72pub fn print_msg<P: AsRef<Path>>(file_path: Option<P>, meta: &Metadata) -> IoResult<()> {
73    #[cfg(feature = "color")]
74    {
75        use std::io::Write as _;
76
77        let stderr = anstream::stderr();
78        let mut stderr = stderr.lock();
79
80        write!(stderr, "{}", anstyle::AnsiColor::Red.render_fg())?;
81        write_msg(&mut stderr, file_path, meta)?;
82        write!(stderr, "{}", anstyle::Reset.render())?;
83    }
84
85    #[cfg(not(feature = "color"))]
86    {
87        let stderr = std::io::stderr();
88        let mut stderr = stderr.lock();
89
90        write_msg(&mut stderr, file_path, meta)?;
91    }
92
93    Ok(())
94}
95
96fn write_msg<P: AsRef<Path>>(
97    buffer: &mut impl std::io::Write,
98    file_path: Option<P>,
99    meta: &Metadata,
100) -> IoResult<()> {
101    let Metadata {
102        name,
103        authors,
104        homepage,
105        repository,
106        support,
107        ..
108    } = meta;
109
110    writeln!(
111        buffer,
112        "{name} had a problem and crashed. To help us diagnose the \
113     problem you can send us a crash report.\n"
114    )?;
115    if let Some(file_path) = file_path {
116        writeln!(
117            buffer,
118            "We have generated a report file at \"{}\". Submit an \
119     issue or email with the subject of \"{} Crash Report\" and include the \
120     report as an attachment.\n",
121            file_path.as_ref().display(),
122            name
123        )?;
124    }
125
126    if let Some(homepage) = homepage {
127        writeln!(buffer, "- Homepage: {homepage}")?;
128    } else if let Some(repository) = repository {
129        writeln!(buffer, "- Repository: {repository}")?;
130    }
131    if let Some(authors) = authors {
132        writeln!(buffer, "- Authors: {authors}")?;
133    }
134    if let Some(support) = support {
135        writeln!(buffer, "\nTo submit the crash report:\n\n{support}")?;
136    }
137    writeln!(
138        buffer,
139        "\nWe take privacy seriously, and do not perform any \
140     automated error collection. In order to improve the software, we rely on \
141     people to submit reports.\n"
142    )?;
143    writeln!(buffer, "Thank you kindly!")?;
144
145    Ok(())
146}
147
148/// Utility function which will handle dumping information to disk
149#[allow(deprecated)]
150pub fn handle_dump(meta: &Metadata, panic_info: &PanicHookInfo<'_>) -> Option<PathBuf> {
151    let report = Report::with_panic(meta, panic_info);
152
153    if let Ok(f) = report.persist() {
154        Some(f)
155    } else {
156        use std::io::Write as _;
157        let stderr = std::io::stderr();
158        let mut stderr = stderr.lock();
159
160        let _ = writeln!(
161            stderr,
162            "{}",
163            report
164                .serialize()
165                .expect("only doing toml compatible types")
166        );
167        None
168    }
169}