use crate::{BoxedError, Error};
use std::{error::Error as StdError, fmt, sync::Arc};
const MAX_REPORTED_ERRORS: usize = 1024;
#[derive(Clone, Debug, Default)]
pub struct ErrorReport {
errors: Vec<Arc<Error>>,
}
impl ErrorReport {
pub fn record(&mut self, error: Error) {
let room = MAX_REPORTED_ERRORS.saturating_sub(self.errors.len());
match error {
Error::Other(cause) => match cause.downcast::<ErrorReport>() {
Ok(report) => self.errors.extend(report.errors.into_iter().take(room)),
Err(cause) if room > 0 => self.errors.push(Arc::new(Error::Other(cause))),
Err(_) => {}
},
error if room > 0 => self.errors.push(Arc::new(error)),
_ => {}
}
}
pub fn record_shared(&mut self, error: Arc<Error>) {
if self.errors.len() < MAX_REPORTED_ERRORS {
self.errors.push(error);
}
}
pub fn errors(&self) -> &[Arc<Error>] {
&self.errors
}
pub fn first(&self) -> Option<&Arc<Error>> {
self.errors.first()
}
pub fn len(&self) -> usize {
self.errors.len()
}
pub fn is_empty(&self) -> bool {
self.errors.is_empty()
}
pub fn into_vec(self) -> Vec<Arc<Error>> {
self.errors
}
pub fn map(self, map: impl FnMut(Arc<Error>) -> Arc<Error>) -> Self {
Self {
errors: self.errors.into_iter().map(map).collect(),
}
}
}
impl From<Error> for ErrorReport {
fn from(error: Error) -> Self {
let mut report = Self::default();
report.record(error);
report
}
}
impl From<crate::ValidationError> for ErrorReport {
fn from(error: crate::ValidationError) -> Self {
Error::from(error).into()
}
}
impl From<std::io::Error> for ErrorReport {
fn from(error: std::io::Error) -> Self {
Error::from(error).into()
}
}
impl From<BoxedError> for ErrorReport {
fn from(error: BoxedError) -> Self {
Error::from(error).into()
}
}
impl From<ErrorReport> for Error {
fn from(report: ErrorReport) -> Self {
let mut errors = report.errors;
if errors.len() == 1 {
match Arc::try_unwrap(errors.remove(0)) {
Ok(error) => return error,
Err(shared) => errors.push(shared),
}
}
Self::Other(Box::new(ErrorReport { errors }))
}
}
impl fmt::Display for ErrorReport {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{} authoring error(s)", self.errors.len())?;
for error in &self.errors {
write!(formatter, "\n {error}")?;
}
Ok(())
}
}
impl StdError for ErrorReport {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.errors
.first()
.map(|error| error.as_ref() as &(dyn StdError + 'static))
}
}
impl IntoIterator for ErrorReport {
type Item = Arc<Error>;
type IntoIter = std::vec::IntoIter<Arc<Error>>;
fn into_iter(self) -> Self::IntoIter {
self.errors.into_iter()
}
}
impl<'a> IntoIterator for &'a ErrorReport {
type Item = &'a Arc<Error>;
type IntoIter = std::slice::Iter<'a, Arc<Error>>;
fn into_iter(self) -> Self::IntoIter {
self.errors.iter()
}
}