not_so_human_panic/
report.rs1use backtrace::Backtrace;
7use serde_derive::Serialize;
8use std::error::Error;
9use std::fmt::Write as FmtWrite;
10use std::mem;
11use std::{env, fs::File, io::Write, path::Path, path::PathBuf};
12use uuid::Uuid;
13
14#[derive(Debug, Serialize, Clone, Copy)]
16pub enum Method {
17 Panic,
19}
20
21#[derive(Debug, Serialize)]
26pub struct Report {
27 name: String,
28 operating_system: String,
29 crate_version: String,
30 explanation: String,
31 cause: String,
32 method: Method,
33 backtrace: String,
34}
35
36impl Report {
37 pub fn new(
39 name: &str,
40 version: &str,
41 method: Method,
42 explanation: String,
43 cause: String,
44 ) -> Self {
45 let operating_system = os_info::get().to_string();
46
47 const SKIP_FRAMES_NUM: usize = 8;
53 const HEX_WIDTH: usize = mem::size_of::<usize>() + 2;
56 const NEXT_SYMBOL_PADDING: usize = HEX_WIDTH + 6;
58
59 let mut backtrace = String::new();
60
61 for (idx, frame) in Backtrace::new()
67 .frames()
68 .iter()
69 .skip(SKIP_FRAMES_NUM)
70 .enumerate()
71 {
72 let ip = frame.ip();
73 let _ = write!(backtrace, "\n{idx:4}: {ip:HEX_WIDTH$?}");
74
75 let symbols = frame.symbols();
76 if symbols.is_empty() {
77 let _ = write!(backtrace, " - <unresolved>");
78 continue;
79 }
80
81 for (idx, symbol) in symbols.iter().enumerate() {
82 if idx != 0 {
86 let _ = write!(backtrace, "\n{:1$}", "", NEXT_SYMBOL_PADDING);
87 }
88
89 if let Some(name) = symbol.name() {
90 let _ = write!(backtrace, " - {name}");
91 } else {
92 let _ = write!(backtrace, " - <unknown>");
93 }
94
95 if let (Some(file), Some(line)) = (symbol.filename(), symbol.lineno()) {
97 let _ = write!(
98 backtrace,
99 "\n{:3$}at {}:{}",
100 "",
101 file.display(),
102 line,
103 NEXT_SYMBOL_PADDING
104 );
105 }
106 }
107 }
108
109 Self {
110 crate_version: version.into(),
111 name: name.into(),
112 operating_system,
113 method,
114 explanation,
115 cause,
116 backtrace,
117 }
118 }
119
120 pub fn serialize(&self) -> Option<String> {
122 toml::to_string_pretty(&self).ok()
123 }
124
125 pub fn persist(&self) -> Result<PathBuf, Box<dyn Error + 'static>> {
127 let uuid = Uuid::new_v4().hyphenated().to_string();
128 let tmp_dir = env::temp_dir();
129 let file_name = format!("report-{}.toml", &uuid);
130 let file_path = Path::new(&tmp_dir).join(file_name);
131 let mut file = File::create(&file_path)?;
132 let toml = self.serialize().unwrap();
133 file.write_all(toml.as_bytes())?;
134 Ok(file_path)
135 }
136}