use crate::{error::Error, span::Span, warning::Warning};
use core::cell::RefCell;
#[derive(Default, Debug)]
pub struct Handler {
inner: RefCell<HandlerInner>,
}
#[derive(Default, Debug)]
struct HandlerInner {
errors: Vec<Error>,
warnings: Vec<Warning>,
}
impl Handler {
pub fn emit_err(&self, err: Error) -> ErrorEmitted {
self.inner.borrow_mut().errors.push(err);
ErrorEmitted { _priv: () }
}
pub fn emit_internal_err<S: Into<String>>(&self, msg: S, span: Span) -> ErrorEmitted {
self.emit_err(Error::Internal {
msg: msg.into(),
span,
})
}
pub fn emit_warn(&self, warning: Warning) {
self.inner.borrow_mut().warnings.push(warning);
}
pub fn cancel(&self) -> ErrorEmitted {
ErrorEmitted { _priv: () }
}
pub fn result<T>(&self, value: T) -> Result<T, ErrorEmitted> {
if self.has_errors() {
Err(self.cancel())
} else {
Ok(value)
}
}
pub fn has_errors(&self) -> bool {
!self.inner.borrow().errors.is_empty()
}
pub fn has_warnings(&self) -> bool {
!self.inner.borrow().warnings.is_empty()
}
pub fn clear(&self) {
self.clear_errors();
self.clear_warnings();
}
pub fn clear_errors(&self) {
self.inner.borrow_mut().errors.clear();
}
pub fn clear_warnings(&self) {
self.inner.borrow_mut().warnings.clear();
}
pub fn remove_internal(&self, always_remove: bool) {
let has_non_internals = !always_remove
&& self
.inner
.borrow()
.errors
.iter()
.any(|err| !matches!(err, Error::Internal { .. }));
if always_remove || has_non_internals {
self.inner
.borrow_mut()
.errors
.retain(|err| !matches!(err, Error::Internal { .. }));
}
}
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<Error>, Vec<Warning>) {
use super::Spanned;
let HandlerInner {
mut errors,
mut warnings,
} = self.inner.into_inner();
let mut seen_errors = fxhash::FxHashSet::default();
errors.retain(|err| {
let sig = err.span().clone();
seen_errors.insert(sig)
});
let mut seen_warnings = fxhash::FxHashSet::default();
warnings.retain(|warn| {
let sig = warn.span().clone();
seen_warnings.insert(sig)
});
(errors, 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);
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ErrorEmitted {
_priv: (),
}