use crate::{error::CompileError, warning::CompileWarning};
use core::cell::RefCell;
#[derive(Default, Debug, Clone)]
pub struct Handler {
inner: RefCell<HandlerInner>,
}
#[derive(Default, Debug, Clone)]
struct HandlerInner {
errors: Vec<CompileError>,
warnings: Vec<CompileWarning>,
}
impl Handler {
pub fn from_parts(errors: Vec<CompileError>, warnings: Vec<CompileWarning>) -> Self {
Self {
inner: RefCell::new(HandlerInner { errors, warnings }),
}
}
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 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);
let had_errors = scoped_handler.has_errors();
self.append(scoped_handler);
if had_errors {
Err(ErrorEmitted { _priv: () })
} else {
closure_res
}
}
pub fn consume(self) -> (Vec<CompileError>, Vec<CompileWarning>) {
let inner = self.inner.into_inner();
(inner.errors, inner.warnings)
}
pub fn append(&self, other: Handler) {
let (errors, warnings) = other.consume();
for warn in warnings {
self.emit_warn(warn);
}
for err in errors {
self.emit_err(err);
}
}
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, 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
}