panic 0.0.0

Please use https://crates.io/crates/panic instead
Documentation
#![cfg_attr(feature = "nightly", deny(missing_docs))]
#![cfg_attr(feature = "nightly", feature(panic_info_message))]

pub mod report;
use report::{Method, Report};

use std::io::Result as IoResult;
use std::panic::PanicInfo;
use std::path::{Path, PathBuf};

struct Write;
pub struct Metadata {
    pub name: &'static str,
    pub short_name: &'static str,
    pub version: &'static str,
    pub repository: &'static str,
}

#[macro_export]
macro_rules! metadata {
    () => {
        Metadata {
            name: env!("CARGO_PKG_NAME").into(),
            short_name: env!("CARGO_PKG_NAME").into(),
            version: env!("CARGO_PKG_VERSION").into(),
            repository: env!("CARGO_PKG_HOMEPAGE").into(),
        }
    };
}

#[macro_export]
macro_rules! setup_panic {
    ($meta:expr) => {
        #[allow(unused_imports)]
        use std::panic::{self, PanicInfo};
        #[allow(unused_imports)]
        use $crate::{handle_dump, print_msg, Metadata};

        match $crate::PanicStyle::default() {
            $crate::PanicStyle::Debug => {}
            $crate::PanicStyle::Human => {
                let meta = $meta;

                panic::set_hook(Box::new(move |info: &PanicInfo| {
                    let file_path = handle_dump(&meta, info);
                    print_msg(file_path, &meta).expect("panic: printing error message to console failed");
                }));
            }
        }
    };

    () => {
        $crate::setup_panic!($crate::metadata!());
    };
}

#[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,
            }
        }
    }
}

#[cfg(feature = "color")]
pub fn print_msg<P: AsRef<Path>>(file_path: Option<P>, meta: &Metadata) -> IoResult<()> {
    use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};

    let stderr_support = concolor::get(concolor::Stream::Stdout);
    let choice = if stderr_support.color() { ColorChoice::Always } else { ColorChoice::Never };
    let stderr = BufferWriter::stderr(choice);
    let mut buffer = stderr.buffer();

    buffer.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
    Write::head(&mut buffer, meta)?;

    buffer.set_color(ColorSpec::new().set_fg(Some(Color::White)))?;
    Write::body(&mut buffer, &file_path, meta)?;

    buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
    Write::footer(&mut buffer)?;

    buffer.reset()?;
    stderr.print(&buffer).unwrap();
    Ok(())
}

#[cfg(not(feature = "color"))]
pub fn print_msg<P: AsRef<Path>>(file_path: Option<P>, meta: &Metadata) -> IoResult<()> {
    let mut buffer = std::io::stderr();

    Write::head(&mut buffer, meta)?;
    Write::body(&mut buffer, &file_path, meta)?;
    Write::footer(&mut buffer)?;

    Ok(())
}

impl Write {
    fn head(buffer: &mut impl std::io::Write, meta: &Metadata) -> IoResult<()> {
        let (name, version) = (&meta.name, &meta.version);
        writeln!(buffer, "Well, this is embarrassing.")?;
        writeln!(
            buffer,
            "{name} v{version} had a problem and crashed. To help us diagnose the \
        problem you can send us a crash report.\n"
        )?;

        Ok(())
    }

    fn body<P: AsRef<Path>>(buffer: &mut impl std::io::Write, file_path: &Option<P>, meta: &Metadata) -> IoResult<()> {
        let (short_name, version, repository) = (&meta.short_name, &meta.version, &meta.repository);
        writeln!(
            buffer,
            "We have generated a report file at \"{}\". Submit an \
          issue or email with the subject of \"{short_name} v{version} crash report\" and include the \
          report as an attachment at {repository}/issues.",
            match file_path {
                Some(fp) => format!("{}", fp.as_ref().display()),
                None => "<Failed to store file to disk>".to_string(),
            },
        )?;

        Ok(())
    }

    fn footer(buffer: &mut impl std::io::Write) -> IoResult<()> {
        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.\nThank you!"
        )?;

        Ok(())
    }
}

pub fn handle_dump(meta: &Metadata, panic_info: &PanicInfo) -> Option<PathBuf> {
    let mut expl = String::new();

    #[cfg(feature = "nightly")]
    let message = panic_info.message().map(|m| format!("{}", m));

    #[cfg(not(feature = "nightly"))]
    let message = match (panic_info.payload().downcast_ref::<&str>(), panic_info.payload().downcast_ref::<String>()) {
        (Some(s), _) => Some(s.to_string()),
        (_, Some(s)) => Some(s.to_string()),
        (None, None) => None,
    };

    let cause = match message {
        Some(m) => m,
        None => "Unknown".into(),
    };

    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 unknown.\n"),
    }

    let report = Report::new(&meta.short_name, &meta.version, Method::Panic, expl, cause);

    match report.persist() {
        Ok(f) => Some(f),
        Err(_) => {
            eprintln!("{}", report.serialize().unwrap());
            None
        }
    }
}