use crate::console::Console;
use crate::panel::Panel;
use crate::style::Style;
use crate::text::Text;
use miette::{Diagnostic, ReportHandler};
use std::fmt;
pub struct GiltMietteHandler {
pub show_code: bool,
pub show_url: bool,
pub show_help: bool,
}
impl GiltMietteHandler {
pub fn new() -> Self {
Self {
show_code: true,
show_url: true,
show_help: true,
}
}
}
impl Default for GiltMietteHandler {
fn default() -> Self {
Self::new()
}
}
impl ReportHandler for GiltMietteHandler {
fn debug(&self, error: &dyn Diagnostic, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let width = f.width().unwrap_or(80);
let mut console = Console::builder()
.width(width)
.force_terminal(true)
.no_color(false)
.build();
console.begin_capture();
let title = format!("{error}");
let mut markup = format!("[bold red]Error:[/bold red] {title}");
if self.show_code {
if let Some(code) = error.code() {
markup.push_str(&format!("\n[dim]Code:[/dim] {code}"));
}
}
if self.show_help {
if let Some(help) = error.help() {
markup.push_str(&format!("\n[bold cyan]Help:[/bold cyan] {help}"));
}
}
if self.show_url {
if let Some(url) = error.url() {
markup.push_str(&format!("\n[blue underline]{url}[/blue underline]"));
}
}
let mut source = std::error::Error::source(error);
if source.is_some() {
markup.push_str("\n\n[bold]Caused by:[/bold]");
}
let mut i = 0;
while let Some(err) = source {
markup.push_str(&format!("\n {i}. {err}"));
source = err.source();
i += 1;
}
let text = Text::from_markup(&markup).unwrap_or_else(|_| Text::new(&markup, Style::null()));
let mut panel = Panel::new(text);
panel.title = Some(Text::new("Diagnostic", Style::null()));
console.print(&panel);
let output = console.end_capture();
write!(f, "{}", output.trim_end_matches('\n'))
}
}
pub fn install() {
miette::set_hook(Box::new(|_| Box::new(GiltMietteHandler::new()))).ok();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_handler_creation() {
let handler = GiltMietteHandler::new();
assert!(handler.show_code);
assert!(handler.show_url);
assert!(handler.show_help);
}
#[test]
fn test_handler_default() {
let handler = GiltMietteHandler::default();
assert!(handler.show_code);
}
#[test]
fn test_handler_formats_simple_error() {
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
#[error("something went wrong")]
#[diagnostic(code(gilt::test::error), help("try doing X instead"))]
struct TestError;
install();
let report = miette::Report::new_boxed(Box::new(TestError));
let output = format!("{report:?}");
assert!(
output.contains("something went wrong"),
"expected error message in output, got: {output}",
);
}
#[test]
fn test_handler_formats_chained_error() {
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
#[error("outer error")]
struct OuterError(#[from] InnerError);
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
#[error("inner error")]
struct InnerError;
let outer = OuterError(InnerError);
let report = miette::Report::new(outer);
let output = format!("{report:?}");
assert!(
output.contains("outer error"),
"expected outer error message in output, got: {output}",
);
}
}