use crate::{
error::CompileError,
warning::{CompileInfo, CompileWarning},
};
use core::cell::RefCell;
#[derive(Default, Debug, Clone)]
pub struct Handler {
inner: RefCell<HandlerDiagnostics>,
}
#[derive(Default, Debug, Clone)]
struct HandlerDiagnostics {
errors: Vec<CompileError>,
warnings: Vec<CompileWarning>,
infos: Vec<CompileInfo>,
}
impl Handler {
pub fn from_parts(
errors: Vec<CompileError>,
warnings: Vec<CompileWarning>,
infos: Vec<CompileInfo>,
) -> Self {
Self {
inner: RefCell::new(HandlerDiagnostics {
errors,
warnings,
infos,
}),
}
}
pub fn emit_err(&self, err: CompileError) -> ErrorEmitted {
self.inner.borrow_mut().errors.push(err);
ErrorEmitted { _priv: () }
}
pub fn cancel(&self) -> ErrorEmitted {
ErrorEmitted { _priv: () }
}
pub fn emit_warn(&self, warn: CompileWarning) {
self.inner.borrow_mut().warnings.push(warn);
}
pub fn emit_info(&self, info: CompileInfo) {
self.inner.borrow_mut().infos.push(info);
}
pub fn has_errors(&self) -> bool {
!self.inner.borrow().errors.is_empty()
}
pub fn find_error(&self, f: impl FnMut(&&CompileError) -> bool) -> Option<CompileError> {
self.inner.borrow().errors.iter().find(f).cloned()
}
pub fn has_warnings(&self) -> bool {
!self.inner.borrow().warnings.is_empty()
}
pub fn scope<T>(
&self,
f: impl FnOnce(&Handler) -> Result<T, ErrorEmitted>,
) -> Result<T, ErrorEmitted> {
let scoped_handler = Handler::default();
let closure_res = f(&scoped_handler);
match self.append(scoped_handler) {
Some(err) => Err(err),
None => closure_res,
}
}
pub fn consume(self) -> (Vec<CompileError>, Vec<CompileWarning>, Vec<CompileInfo>) {
let inner = self.inner.into_inner();
(inner.errors, inner.warnings, inner.infos)
}
pub fn append(&self, other: Handler) -> Option<ErrorEmitted> {
let other_has_errors = other.has_errors();
let (errors, warnings, infos) = other.consume();
for warn in warnings {
self.emit_warn(warn);
}
for err in errors {
self.emit_err(err);
}
for inf in infos {
self.emit_info(inf);
}
if other_has_errors {
Some(ErrorEmitted { _priv: () })
} else {
None
}
}
pub fn dedup(&self) {
let mut inner = self.inner.borrow_mut();
inner.errors = dedup_unsorted(inner.errors.clone());
inner.warnings = dedup_unsorted(inner.warnings.clone());
}
pub fn retain_err<F>(&self, f: F)
where
F: FnMut(&CompileError) -> bool,
{
self.inner.borrow_mut().errors.retain(f)
}
pub fn map_and_emit_errors_from(
&self,
other: Handler,
mut f: impl FnMut(CompileError) -> Option<CompileError>,
) -> Result<(), ErrorEmitted> {
let mut emitted = Ok(());
let (errs, _, _) = other.consume();
for err in errs {
if let Some(err) = (f)(err) {
emitted = Err(self.emit_err(err));
}
}
emitted
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ErrorEmitted {
_priv: (),
}
fn dedup_unsorted<T: PartialEq + std::hash::Hash + Clone + Eq>(mut data: Vec<T>) -> Vec<T> {
use std::collections::HashSet;
let mut seen = HashSet::new();
data.retain(|item| seen.insert(item.clone()));
data
}