shdrlib 0.1.5

A three-tiered Vulkan shader compilation and rendering framework built in pure Rust
Documentation
//! Error formatting and display utilities
//!
//! This module provides rich error formatting for better debugging experiences.

use std::fmt;

/// Color codes for terminal output (if supported)
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";
}

/// Format an error with a full diagnostic box
pub fn format_error_diagnostic(
    title: &str,
    error: &dyn std::error::Error,
    additional_context: Option<&str>,
) -> String {
    use colors::*;

    let mut output = String::new();

    // Header
    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));

    // Error message
    output.push_str(&format!("{}{}{}\n\n", RED, error, RESET));

    // Additional context
    if let Some(ctx) = additional_context {
        output.push_str(&format!("{}{}Context:{}\n", BOLD, BLUE, RESET));
        output.push_str(&format!("{}\n\n", ctx));
    }

    // Error chain
    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');
    }

    // Footer
    output.push_str(&format!("{}{}", BOLD, RED));
    output.push_str(&"".repeat(68));
    output.push_str(&format!("{}\n", RESET));

    output
}

/// Print error with diagnostic information to stderr
pub fn print_error_diagnostic(
    title: &str,
    error: &dyn std::error::Error,
    additional_context: Option<&str>,
) {
    eprintln!("{}", format_error_diagnostic(title, error, additional_context));
}

/// Helper for quickly printing an error result
pub trait PrintError<T> {
    /// Print the error if present and return the result
    fn print_error(self, context: &str) -> Self;
    
    /// Print the error and panic if present
    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);
            }
        }
    }
}

/// Format a validation error in a special format
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));

    // Message ID
    output.push_str(&format!("{}{}Message ID:{} {}\n\n", BOLD, BLUE, RESET, message_id));

    // Message
    output.push_str(&format!("{}{}{}\n\n", severity_color, message, RESET));

    // Objects involved
    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');
    }

    // Footer
    output.push_str(&format!("{}{}", BOLD, severity_color));
    output.push_str(&"".repeat(68));
    output.push_str(&format!("{}\n", RESET));

    output
}

/// Create a simple error report with recommendations
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"));
    }
}