Skip to main content

crossbuild_core/
diagnostics.rs

1//! Diagnostics system for crossbuild.
2
3use std::fmt::{self, Display, Formatter};
4use std::io::{self, Write};
5
6
7/// Diagnostic severity used by the engine and CLI.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Severity {
10    Info,
11    Warning,
12    Error,
13}
14
15impl Display for Severity {
16    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
17        match self {
18            Severity::Info => f.write_str("info"),
19            Severity::Warning => f.write_str("warning"),
20            Severity::Error => f.write_str("error"),
21        }
22    }
23}
24
25/// A structured diagnostic event.
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct Diagnostic {
28    pub severity: Severity,
29    pub code: &'static str,
30    pub message: String,
31    pub help: Option<String>,
32}
33
34impl Diagnostic {
35    pub fn info(code: &'static str, message: impl Into<String>) -> Self {
36        Self {
37            severity: Severity::Info,
38            code,
39            message: message.into(),
40            help: None,
41        }
42    }
43
44    pub fn warning(code: &'static str, message: impl Into<String>) -> Self {
45        Self {
46            severity: Severity::Warning,
47            code,
48            message: message.into(),
49            help: None,
50        }
51    }
52
53    pub fn error(code: &'static str, message: impl Into<String>) -> Self {
54        Self {
55            severity: Severity::Error,
56            code,
57            message: message.into(),
58            help: None,
59        }
60    }
61
62    pub fn with_help(mut self, help: impl Into<String>) -> Self {
63        self.help = Some(help.into());
64        self
65    }
66}
67
68/// Receives diagnostics emitted by the engine.
69pub trait DiagnosticSink: Send + Sync {
70    fn emit(&mut self, diagnostic: Diagnostic);
71}
72
73/// Writes diagnostics to stderr in a stable, human-readable format.
74pub struct StderrDiagnosticSink {
75    verbose: bool,
76}
77
78impl StderrDiagnosticSink {
79    pub fn new(verbose: bool) -> Self {
80        Self { verbose }
81    }
82}
83
84impl DiagnosticSink for StderrDiagnosticSink {
85    fn emit(&mut self, diagnostic: Diagnostic) {
86        let severity = match diagnostic.severity {
87            Severity::Info => "info",
88            Severity::Warning => "warning",
89            Severity::Error => "error",
90        };
91
92        let mut stderr = io::stderr().lock();
93        let _ = writeln!(
94            stderr,
95            "[{severity}] {}: {}",
96            diagnostic.code, diagnostic.message
97        );
98        if self.verbose {
99            if let Some(help) = diagnostic.help {
100                let _ = writeln!(stderr, "  help: {help}");
101            }
102        }
103    }
104}