use std::io::Result as IoResult;
use std::panic::PanicHookInfo;
use std::path::{Path, PathBuf};
use crate::Metadata;
use crate::report::Report;
#[doc(hidden)]
pub fn setup_panic(meta: impl Fn() -> Metadata) {
#![allow(deprecated)]
#[allow(unused_imports)]
use std::panic;
match PanicStyle::default() {
PanicStyle::Debug => {}
PanicStyle::Human => {
let meta = meta();
panic::set_hook(Box::new(move |info: &PanicHookInfo<'_>| {
let report = Report::with_panic(&meta, info);
let file_path = if is_ci() { None } else { report.persist().ok() };
if file_path.is_none() {
use std::io::Write as _;
let stderr = std::io::stderr();
let mut stderr = stderr.lock();
let _ = writeln!(
stderr,
"{}",
report
.serialize()
.expect("only doing toml compatible types")
);
}
print_msg(file_path.as_deref(), &meta)
.expect("human-panic: printing error message to console failed");
}));
}
}
}
fn is_ci() -> bool {
std::env::var_os("CI").is_some()
}
#[non_exhaustive]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum PanicStyle {
Debug,
Human,
}
impl Default for PanicStyle {
fn default() -> Self {
if cfg!(debug_assertions) {
PanicStyle::Debug
} else {
match ::std::env::var("RUST_BACKTRACE") {
Ok(_) => PanicStyle::Debug,
Err(_) => PanicStyle::Human,
}
}
}
}
pub fn print_msg<P: AsRef<Path>>(file_path: Option<P>, meta: &Metadata) -> IoResult<()> {
#[cfg(feature = "color")]
{
use std::io::Write as _;
let stderr = anstream::stderr();
let mut stderr = stderr.lock();
write!(stderr, "{}", anstyle::AnsiColor::Red.render_fg())?;
write_msg(&mut stderr, file_path, meta)?;
write!(stderr, "{}", anstyle::Reset.render())?;
}
#[cfg(not(feature = "color"))]
{
let stderr = std::io::stderr();
let mut stderr = stderr.lock();
write_msg(&mut stderr, file_path, meta)?;
}
Ok(())
}
fn write_msg<P: AsRef<Path>>(
buffer: &mut impl std::io::Write,
file_path: Option<P>,
meta: &Metadata,
) -> IoResult<()> {
let Metadata {
name,
authors,
homepage,
repository,
support,
..
} = meta;
writeln!(
buffer,
"{name} had a problem and crashed. To help us diagnose the \
problem you can send us a crash report.\n"
)?;
if let Some(file_path) = file_path {
writeln!(
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 let Some(homepage) = homepage {
writeln!(buffer, "- Homepage: {homepage}")?;
} else if let Some(repository) = repository {
writeln!(buffer, "- Repository: {repository}")?;
}
if let Some(authors) = authors {
writeln!(buffer, "- Authors: {authors}")?;
}
if let Some(support) = support {
writeln!(buffer, "\nTo submit the crash report:\n\n{support}")?;
}
writeln!(
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!(buffer, "Thank you kindly!")?;
Ok(())
}
#[allow(deprecated)]
pub fn handle_dump(meta: &Metadata, panic_info: &PanicHookInfo<'_>) -> Option<PathBuf> {
let report = Report::with_panic(meta, panic_info);
if let Ok(f) = report.persist() {
Some(f)
} else {
use std::io::Write as _;
let stderr = std::io::stderr();
let mut stderr = stderr.lock();
let _ = writeln!(
stderr,
"{}",
report
.serialize()
.expect("only doing toml compatible types")
);
None
}
}