use crate::Diagnostic;
use crate::Severity;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct DiagnosticAbort;
pub type DiagnosticResult<T> = Result<T, DiagnosticAbort>;
pub trait DiagnosticSink {
fn emit(&mut self, diagnostic: Diagnostic) -> DiagnosticResult<()>;
}
#[derive(Default, Debug)]
pub struct CollectingSink {
diagnostics: Vec<Diagnostic>,
had_error: bool,
}
impl CollectingSink {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn had_error(&self) -> bool {
self.had_error
}
#[must_use]
pub fn diagnostics(&self) -> &[Diagnostic] {
&self.diagnostics
}
#[must_use]
pub fn into_diagnostics(self) -> Vec<Diagnostic> {
self.diagnostics
}
}
impl DiagnosticSink for CollectingSink {
fn emit(&mut self, diagnostic: Diagnostic) -> DiagnosticResult<()> {
if diagnostic.severity() == Severity::Error {
self.had_error = true;
}
self.diagnostics.push(diagnostic);
Ok(())
}
}
impl<S: DiagnosticSink + ?Sized> DiagnosticSink for &mut S {
fn emit(&mut self, diagnostic: Diagnostic) -> DiagnosticResult<()> {
(**self).emit(diagnostic)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codes;
#[test]
fn collecting_sink_tracks_error_flag_and_order() {
let mut sink = CollectingSink::new();
assert!(!sink.had_error());
assert!(
sink.emit(Diagnostic::simple(&codes::MOS0018, None, "notice"))
.is_ok(),
"collecting sink must not abort"
);
assert!(!sink.had_error(), "a notice must not flip had_error");
assert!(
sink.emit(Diagnostic::simple(&codes::MOS0028, None, "warning"))
.is_ok(),
"collecting sink must not abort"
);
assert!(!sink.had_error(), "a warning must not flip had_error");
assert!(
sink.emit(Diagnostic::simple(&codes::MOS0010, None, "error"))
.is_ok(),
"collecting sink must not abort"
);
assert!(sink.had_error(), "an error must flip had_error");
let diags = sink.into_diagnostics();
assert_eq!(diags.len(), 3);
assert_eq!(diags[0].def().code(), codes::MOS0018.code());
assert_eq!(diags[2].severity(), Severity::Error);
}
}