use std::fmt;
pub mod colors {
pub const RESET: &str = "\x1b[0m";
pub const RED: &str = "\x1b[31m";
pub const YELLOW: &str = "\x1b[33m";
pub const BLUE: &str = "\x1b[34m";
pub const GREEN: &str = "\x1b[32m";
pub const BOLD: &str = "\x1b[1m";
pub const DIM: &str = "\x1b[2m";
}
pub fn format_error_diagnostic(
title: &str,
error: &dyn std::error::Error,
additional_context: Option<&str>,
) -> String {
use colors::*;
let mut output = String::new();
output.push_str(&format!("\n{}{}╔", BOLD, RED));
output.push_str(&"═".repeat(68));
output.push_str(&format!("╗{}\n", RESET));
output.push_str(&format!("{}{}║{} 🚨 {} ", BOLD, RED, RESET, title));
let padding = 68 - title.len() - 4;
output.push_str(&" ".repeat(padding));
output.push_str(&format!("{}{}║{}\n", BOLD, RED, RESET));
output.push_str(&format!("{}{}╠", BOLD, RED));
output.push_str(&"═".repeat(68));
output.push_str(&format!("╣{}\n\n", RESET));
output.push_str(&format!("{}{}{}\n\n", RED, error, RESET));
if let Some(ctx) = additional_context {
output.push_str(&format!("{}{}Context:{}\n", BOLD, BLUE, RESET));
output.push_str(&format!("{}\n\n", ctx));
}
let mut source = error.source();
if source.is_some() {
output.push_str(&format!("{}{}Caused by:{}\n", BOLD, YELLOW, RESET));
let mut depth = 1;
while let Some(err) = source {
output.push_str(&format!("{} {}. {}{}\n", DIM, depth, err, RESET));
source = err.source();
depth += 1;
}
output.push('\n');
}
output.push_str(&format!("{}{}╚", BOLD, RED));
output.push_str(&"═".repeat(68));
output.push_str(&format!("╝{}\n", RESET));
output
}
pub fn print_error_diagnostic(
title: &str,
error: &dyn std::error::Error,
additional_context: Option<&str>,
) {
eprintln!("{}", format_error_diagnostic(title, error, additional_context));
}
pub trait PrintError<T> {
fn print_error(self, context: &str) -> Self;
fn expect_or_print(self, context: &str) -> T;
}
impl<T, E: std::error::Error> PrintError<T> for Result<T, E> {
fn print_error(self, context: &str) -> Self {
if let Err(ref e) = self {
print_error_diagnostic(context, e, None);
}
self
}
fn expect_or_print(self, context: &str) -> T {
match self {
Ok(v) => v,
Err(e) => {
print_error_diagnostic(context, &e, None);
panic!("Fatal error: {}", context);
}
}
}
}
pub fn format_validation_error(
message_id: &str,
severity: &str,
message: &str,
objects: &[String],
) -> String {
use colors::*;
let mut output = String::new();
let (severity_color, severity_icon) = match severity.to_uppercase().as_str() {
"ERROR" => (RED, "❌"),
"WARNING" => (YELLOW, "⚠️"),
"INFO" => (BLUE, "ℹ️"),
_ => (RESET, "•"),
};
output.push_str(&format!("\n{}{}╔", BOLD, severity_color));
output.push_str(&"═".repeat(68));
output.push_str(&format!("╗{}\n", RESET));
output.push_str(&format!(
"{}{}║{} {} VULKAN VALIDATION {} ",
BOLD, severity_color, RESET, severity_icon, severity
));
let padding = 68 - 21 - severity.len() - 3;
output.push_str(&" ".repeat(padding));
output.push_str(&format!("{}{}║{}\n", BOLD, severity_color, RESET));
output.push_str(&format!("{}{}╠", BOLD, severity_color));
output.push_str(&"═".repeat(68));
output.push_str(&format!("╣{}\n\n", RESET));
output.push_str(&format!("{}{}Message ID:{} {}\n\n", BOLD, BLUE, RESET, message_id));
output.push_str(&format!("{}{}{}\n\n", severity_color, message, RESET));
if !objects.is_empty() {
output.push_str(&format!("{}{}Objects involved:{}\n", BOLD, BLUE, RESET));
for obj in objects {
output.push_str(&format!(" • {}\n", obj));
}
output.push('\n');
}
output.push_str(&format!("{}{}╚", BOLD, severity_color));
output.push_str(&"═".repeat(68));
output.push_str(&format!("╝{}\n", RESET));
output
}
pub struct ErrorReport {
pub title: String,
pub description: String,
pub causes: Vec<String>,
pub solutions: Vec<String>,
pub references: Vec<String>,
}
impl ErrorReport {
pub fn new(title: impl Into<String>, description: impl Into<String>) -> Self {
Self {
title: title.into(),
description: description.into(),
causes: Vec::new(),
solutions: Vec::new(),
references: Vec::new(),
}
}
pub fn add_cause(&mut self, cause: impl Into<String>) {
self.causes.push(cause.into());
}
pub fn add_solution(&mut self, solution: impl Into<String>) {
self.solutions.push(solution.into());
}
pub fn add_reference(&mut self, reference: impl Into<String>) {
self.references.push(reference.into());
}
}
impl fmt::Display for ErrorReport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use colors::*;
writeln!(f, "\n{}{}╔", BOLD, RED)?;
writeln!(f, "═════════════════════════════════════════════════════════════════╗{}", RESET)?;
writeln!(f, "{}{}║{} {} ", BOLD, RED, RESET, self.title)?;
writeln!(f, "{}{}╠═════════════════════════════════════════════════════════════════╣{}", BOLD, RED, RESET)?;
writeln!(f)?;
writeln!(f, "{}", self.description)?;
writeln!(f)?;
if !self.causes.is_empty() {
writeln!(f, "{}{}Possible Causes:{}", BOLD, YELLOW, RESET)?;
for cause in &self.causes {
writeln!(f, " • {}", cause)?;
}
writeln!(f)?;
}
if !self.solutions.is_empty() {
writeln!(f, "{}{}Solutions:{}", BOLD, GREEN, RESET)?;
for (i, solution) in self.solutions.iter().enumerate() {
writeln!(f, " {}. {}", i + 1, solution)?;
}
writeln!(f)?;
}
if !self.references.is_empty() {
writeln!(f, "{}{}Learn More:{}", BOLD, BLUE, RESET)?;
for reference in &self.references {
writeln!(f, " → {}", reference)?;
}
writeln!(f)?;
}
writeln!(f, "{}{}╚═════════════════════════════════════════════════════════════════╝{}", BOLD, RED, RESET)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_report_display() {
let mut report = ErrorReport::new("Test Error", "Something went wrong");
report.add_cause("Bad configuration");
report.add_solution("Fix the config");
report.add_reference("https://docs.example.com");
let output = format!("{}", report);
assert!(output.contains("Test Error"));
assert!(output.contains("Bad configuration"));
assert!(output.contains("Fix the config"));
}
}