use std::fmt::{self, Display, Formatter};
use std::io::{self, Write};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Severity {
Info,
Warning,
Error,
}
impl Display for Severity {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Severity::Info => f.write_str("info"),
Severity::Warning => f.write_str("warning"),
Severity::Error => f.write_str("error"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Diagnostic {
pub severity: Severity,
pub code: &'static str,
pub message: String,
pub help: Option<String>,
}
impl Diagnostic {
pub fn info(code: &'static str, message: impl Into<String>) -> Self {
Self {
severity: Severity::Info,
code,
message: message.into(),
help: None,
}
}
pub fn warning(code: &'static str, message: impl Into<String>) -> Self {
Self {
severity: Severity::Warning,
code,
message: message.into(),
help: None,
}
}
pub fn error(code: &'static str, message: impl Into<String>) -> Self {
Self {
severity: Severity::Error,
code,
message: message.into(),
help: None,
}
}
pub fn with_help(mut self, help: impl Into<String>) -> Self {
self.help = Some(help.into());
self
}
}
pub trait DiagnosticSink: Send + Sync {
fn emit(&mut self, diagnostic: Diagnostic);
}
pub struct StderrDiagnosticSink {
verbose: bool,
}
impl StderrDiagnosticSink {
pub fn new(verbose: bool) -> Self {
Self { verbose }
}
}
impl DiagnosticSink for StderrDiagnosticSink {
fn emit(&mut self, diagnostic: Diagnostic) {
let severity = match diagnostic.severity {
Severity::Info => "info",
Severity::Warning => "warning",
Severity::Error => "error",
};
let mut stderr = io::stderr().lock();
let _ = writeln!(
stderr,
"[{severity}] {}: {}",
diagnostic.code, diagnostic.message
);
if self.verbose {
if let Some(help) = diagnostic.help {
let _ = writeln!(stderr, " help: {help}");
}
}
}
}