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
43fn is_ci() -> bool {
45 std::env::var_os("CI").is_some()
46}
47
48#[non_exhaustive]
50#[derive(Copy, Clone, PartialEq, Eq)]
51pub enum PanicStyle {
52 Debug,
54 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
71pub 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#[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}