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
//! Implements `PretyError` to print pretty errors in the CLI (when they happen)
use anyhow::{Chain, Error};
use colored::*;
use std::fmt::{self, Debug, Write};
/// A `PrettyError` for printing `anyhow::Error` nicely.
pub struct PrettyError {
error: Error,
}
/// A macro that prints a warning with nice colors
#[macro_export]
macro_rules! warning {
($($arg:tt)*) => ({
use colored::*;
eprintln!("{}: {}", "warning".yellow().bold(), format!($($arg)*));
})
}
impl PrettyError {
/// Process a `Result` printing any errors and exiting
/// the process after
pub fn report<T>(result: Result<T, Error>) -> ! {
std::process::exit(match result {
Ok(_t) => 0,
Err(error) => {
eprintln!("{:?}", PrettyError { error });
1
}
});
}
}
impl Debug for PrettyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let error = &self.error;
if f.alternate() {
return Debug::fmt(&error, f);
}
write!(f, "{}", format!("{}: {}", "error".red(), error).bold())?;
// write!(f, "{}", error)?;
if let Some(cause) = error.source() {
// write!(f, "\n{}:", "caused by".bold().blue())?;
let chain = Chain::new(cause);
let (total_errors, _) = chain.size_hint();
for (n, error) in chain.enumerate() {
writeln!(f)?;
let mut indented = Indented {
inner: f,
number: Some(n + 1),
is_last: n == total_errors - 1,
started: false,
};
write!(indented, "{error}")?;
}
}
Ok(())
}
}
struct Indented<'a, D> {
inner: &'a mut D,
number: Option<usize>,
started: bool,
is_last: bool,
}
impl<T> Write for Indented<'_, T>
where
T: Write,
{
fn write_str(&mut self, s: &str) -> fmt::Result {
for (i, line) in s.split('\n').enumerate() {
if !self.started {
self.started = true;
match self.number {
Some(number) => {
if !self.is_last {
write!(
self.inner,
"{} {: >4} ",
"│".bold().blue(),
format!("{number}:").dimmed()
)?
} else {
write!(
self.inner,
"{}{: >2}: ",
"╰─▶".bold().blue(),
format!("{number}").bold().blue()
)?
}
}
None => self.inner.write_str(" ")?,
}
} else if i > 0 {
self.inner.write_char('\n')?;
if self.number.is_some() {
self.inner.write_str(" ")?;
} else {
self.inner.write_str(" ")?;
}
}
self.inner.write_str(line)?;
}
Ok(())
}
}