/
lib.rs
119 lines (102 loc) · 3.31 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#![cfg_attr(feature = "nightly", deny(missing_docs))]
#![cfg_attr(feature = "nightly", feature(external_doc))]
#![cfg_attr(feature = "nightly", doc(include = "../README.md"))]
#[macro_use]
extern crate failure;
#[macro_use]
extern crate serde_derive;
extern crate termcolor;
mod report;
use report::{Method, Report};
use failure::Error as FailError;
use std::io::{Result as IoResult, Write};
use std::path::{Path, PathBuf};
use std::panic::PanicInfo;
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
/// A convenient metadata struct that describes a crate
pub struct Metadata<'a> {
pub version: &'a str,
pub name: &'a str,
pub authors: &'a str,
pub homepage: &'a str,
}
/// Setup the human panic hook that will make all panics
/// as beautiful as your shitty code.
#[macro_export]
macro_rules! setup_panic {
() => {
use human_panic::*;
use std::panic::{self, PanicInfo};
let meta = Metadata {
version: env!("CARGO_PKG_VERSION"),
name: env!("CARGO_PKG_NAME"),
authors: env!("CARGO_PKG_AUTHORS"),
homepage: env!("CARGO_PKG_HOMEPAGE"),
};
panic::set_hook(Box::new(move |info: &PanicInfo| {
let file_path =
handle_dump(info).expect("human-panic: dumping logs to disk failed");
print_msg(&file_path, &meta)
.expect("human-panic: printing error message to console failed");
}));
};
}
/// Utility function that prints a message to our human users
pub fn print_msg<P: AsRef<Path>>(
file_path: P,
meta: &Metadata,
) -> IoResult<()> {
let (_version, name, authors, homepage) =
(meta.version, meta.name, meta.authors, meta.homepage);
let stderr = BufferWriter::stderr(ColorChoice::Auto);
let mut buffer = stderr.buffer();
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
writeln!(&mut buffer, "Well, this is embarrasing.\n")?;
writeln!(
&mut buffer,
"{} had a problem and crashed. To help us diagnose the \
problem you can send us a crash report.\n",
name
)?;
writeln!(
&mut buffer,
"We have generated a report file at \"{}\". Submit an \
issue or email with the subject of \"{} Crash Report\" and include the \
report as an attachment.\n",
file_path.as_ref().display(),
name
)?;
if !homepage.is_empty() {
writeln!(&mut buffer, "- Homepage: {}", homepage)?;
}
if !authors.is_empty() {
writeln!(&mut buffer, "- Authors: {}", authors)?;
}
writeln!(
&mut buffer,
"\nWe take privacy seriously, and do not perform any \
automated error collection. In order to improve the software, we rely on \
people to submit reports.\n"
)?;
writeln!(&mut buffer, "Thank you kindly!")?;
stderr.print(&buffer).unwrap();
Ok(())
}
/// Utility function which will handle dumping information to disk
pub fn handle_dump(panic_info: &PanicInfo) -> Result<PathBuf, FailError> {
let mut expl = String::new();
let payload = panic_info.payload().downcast_ref::<&str>();
if let Some(payload) = payload {
expl.push_str(&format!("Cause: {}. ", &payload));
}
match panic_info.location() {
Some(location) => expl.push_str(&format!(
"Panic occurred in file '{}' at line {}\n",
location.file(),
location.line()
)),
None => expl.push_str("Panic location uknown.\n"),
}
let report = Report::new(Method::Panic, expl);
report.persist()
}