use std::error::Error;
use std::fmt;
use std::slice::Iter;
use crate::Many;
pub struct ErrorReport<'a>(pub &'a (dyn Error + 'static));
impl<'a> ErrorReport<'a> {
pub fn new(error: &'a (dyn Error + 'static)) -> Self {
Self(error)
}
}
impl fmt::Display for ErrorReport<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write_error(f, self.0, 0, "")
}
}
enum UnifiedChildren<'a> {
Slice(&'a [Box<dyn Error + Send + Sync + 'static>]),
Single(Option<&'a (dyn Error + 'static)>),
}
enum ErrorIter<'a> {
Slice(Iter<'a, Box<dyn Error + Send + Sync + 'static>>),
Single(Option<&'a (dyn Error + 'static)>),
}
impl<'a> Iterator for ErrorIter<'a> {
type Item = &'a (dyn Error + 'static);
fn next(&mut self) -> Option<Self::Item> {
match self {
ErrorIter::Slice(iter) => iter.next().map(|b| b.as_ref() as _),
ErrorIter::Single(opt) => opt.take(),
}
}
}
impl<'a> UnifiedChildren<'a> {
fn from(error: &'a (dyn Error + 'static)) -> Self {
if let Some(multi) = error.downcast_ref::<Many>() {
UnifiedChildren::Slice(multi.causes())
} else {
UnifiedChildren::Single(error.source())
}
}
fn len(&self) -> usize {
match self {
UnifiedChildren::Slice(s) => s.len(),
UnifiedChildren::Single(opt) => opt.iter().len(),
}
}
fn iter(&self) -> ErrorIter<'a> {
match *self {
UnifiedChildren::Slice(slice) => ErrorIter::Slice(slice.iter()),
UnifiedChildren::Single(opt) => ErrorIter::Single(opt),
}
}
}
pub fn write_error(
f: &mut fmt::Formatter<'_>,
error: &(dyn Error + 'static),
level: usize,
prefix: &str,
) -> fmt::Result {
write!(f, "{}", error)?;
let children = UnifiedChildren::from(error);
let children_len = children.len();
for (i, child) in children.iter().enumerate() {
let child_child_len = UnifiedChildren::from(child).len();
let is_linear = level == 0 && children_len == 1 && child_child_len == 1;
if i != children_len - 1 || is_linear {
write!(f, "\n{}|-- ", prefix)?;
} else {
write!(f, "\n{}`-- ", prefix)?;
}
if is_linear {
write_error(f, child, 0, prefix)?;
} else if i < children_len - 1 {
write_error(f, child, level + 1, &format!("{}| ", prefix))?;
} else {
write_error(f, child, level + 1, &format!("{} ", prefix))?;
}
}
Ok(())
}