error-engine 0.2.0

A message catalog + presentation layer on top of thiserror and tracing for consistent error/warning/info rendering.
Documentation
use crate::{catalog::Catalog, diagnostic::{EngineDiagnostic, Severity}};
use tracing::{error, warn, info};

pub struct Engine {
    catalog: Catalog,
}

impl Engine {
    pub fn new(catalog: Catalog) -> Self {
        Self { catalog }
    }

    /// Plain text, no color, no logging. For GUI apps.
    pub fn message(&self, diag: &dyn EngineDiagnostic) -> String {
        self.catalog.render(diag.code(), &diag.context())
    }

    /// Sends to `tracing` at the matching level. For file/service logging.
    pub fn log(&self, diag: &dyn EngineDiagnostic) {
        let msg = self.message(diag);
        match diag.severity() {
            Severity::Error => error!(code = diag.code(), "{msg}"),
            Severity::Warning => warn!(code = diag.code(), "{msg}"),
            Severity::Info => info!(code = diag.code(), "{msg}"),
        }
    }

    /// Colored terminal output, with hint if present. Requires the "cli" feature.
    #[cfg(feature = "cli")]
    pub fn print(&self, diag: &dyn EngineDiagnostic) {
        use owo_colors::OwoColorize;

        let msg = self.message(diag);
        let hint = self.catalog.hint(diag.code());

        match diag.severity() {
            Severity::Error => eprintln!("{} {msg}", "".red().bold()),
            Severity::Warning => eprintln!("{} {msg}", "".yellow().bold()),
            Severity::Info => println!("{} {msg}", "".blue().bold()),
        }
        if let Some(hint) = hint {
            eprintln!("  {} {hint}", "".dimmed());
        }
    }
}