use crate::{Diagnostic, Renderer, Severity};
pub enum DrainError {
Fmt(std::fmt::Error),
CompoundError(usize),
}
impl From<std::fmt::Error> for DrainError {
fn from(err: std::fmt::Error) -> Self {
Self::Fmt(err)
}
}
impl std::error::Error for DrainError {}
impl std::fmt::Debug for DrainError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Fmt(e) => e.fmt(f),
Self::CompoundError(cnt) => f.debug_tuple("CompoundError").field(cnt).finish(),
}
}
}
impl std::fmt::Display for DrainError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Fmt(e) => e.fmt(f),
Self::CompoundError(cnt) => write!(f, "aborting due to {cnt} previous errors"),
}
}
}
pub trait Handler: std::any::Any {
fn report(&mut self, diagnostic: Box<dyn Diagnostic>);
fn drain(&mut self) -> Result<(), DrainError>;
fn report_and_drain(&mut self, diagnostic: Box<dyn Diagnostic>) -> Result<(), DrainError> {
self.report(diagnostic);
self.drain()
}
}
pub struct DiagnosticHandler {
exit_on_error: bool,
emitted_diagnostics: Vec<Box<dyn Diagnostic>>,
renderer: Box<dyn Renderer + Send + Sync>,
}
impl DiagnosticHandler {
pub fn with_renderer(renderer: Box<dyn Renderer + Send + Sync>) -> Self {
DiagnosticHandler {
exit_on_error: false,
emitted_diagnostics: Vec::new(),
renderer,
}
}
pub fn exit_on_error(&mut self) {
self.exit_on_error = true
}
pub fn emitted(&self) -> impl Iterator<Item = &Box<dyn Diagnostic>> {
self.emitted_diagnostics.iter()
}
pub fn count(&self) -> usize {
self.emitted_diagnostics.len()
}
}
impl Handler for DiagnosticHandler {
fn report(&mut self, diagnostic: Box<dyn Diagnostic>) {
self.emitted_diagnostics.push(diagnostic);
}
fn drain(&mut self) -> Result<(), DrainError> {
let mut encountered_errors = 0usize;
for diagnostic in self.emitted_diagnostics.drain(..) {
self.renderer.render_stderr(diagnostic.as_ref())?;
if diagnostic.severity() == Severity::Error {
encountered_errors += 1;
}
}
if encountered_errors > 0 && self.exit_on_error {
return Err(DrainError::CompoundError(encountered_errors));
}
Ok(())
}
}
pub struct BufferedDiagnosticHandler {
buffer: String,
emitted_diagnostics: Vec<Box<dyn Diagnostic>>,
renderer: Box<dyn Renderer + Send + Sync>,
}
impl BufferedDiagnosticHandler {
pub fn with_renderer(capacity: usize, renderer: Box<dyn Renderer + Send + Sync>) -> Self {
Self {
buffer: String::with_capacity(capacity),
emitted_diagnostics: Vec::new(),
renderer,
}
}
pub fn buffer(&self) -> &str {
&self.buffer
}
pub fn emitted(&self) -> impl Iterator<Item = &Box<dyn Diagnostic>> {
self.emitted_diagnostics.iter()
}
pub fn count(&self) -> usize {
self.emitted_diagnostics.len()
}
}
impl Handler for BufferedDiagnosticHandler {
fn report(&mut self, diagnostic: Box<dyn Diagnostic>) {
self.emitted_diagnostics.push(diagnostic);
}
fn drain(&mut self) -> Result<(), DrainError> {
for diagnostic in self.emitted_diagnostics.drain(..) {
let rendered = self.renderer.render(diagnostic.as_ref())?;
self.buffer.push_str(&rendered);
}
Ok(())
}
}