use core::{fmt, marker::PhantomData, ops::ControlFlow};
use derail::{Error, VisitContext, Visitor, VisitorExt as _};
use crate::{BufferFactory, buffer::Buffer, sealed::Sealed};
mod unicode {
#![allow(clippy::missing_docs_in_private_items)]
pub(super) const BLACK_RIGHT_POINTING_POINTER: char = '\u{25BA}';
pub(super) const BLACK_SQUARE: char = '\u{25A0}';
pub(super) const LIGHT_ARC_UP_AND_RIGHT: char = '\u{2570}';
pub(super) const LIGHT_HORIZONTAL: char = '\u{2500}';
pub(super) const LIGHT_VERTICAL: char = '\u{2502}';
pub(super) const LIGHT_VERTICAL_AND_RIGHT: char = '\u{251C}';
}
#[derive(Clone, Copy, Default)]
struct Element {
is_last_sibling: bool,
}
#[cfg_attr(
feature = "alloc",
doc = "\n\n[`HeapFactory`](crate::HeapFactory) does not have this \
limitation."
)]
pub fn display<'a, I, E, B>(errors: I) -> impl fmt::Display
where
I: IntoIterator<Item = &'a E> + Clone,
E: Error + ?Sized + 'a,
B: BufferFactory,
{
DisplayImpl(errors, PhantomData::<B>)
}
struct DisplayImpl<I, B>(I, PhantomData<B>);
impl<'a, I, E, B> fmt::Display for DisplayImpl<I, B>
where
I: IntoIterator<Item = &'a E> + Clone,
E: Error + ?Sized + 'a,
B: BufferFactory,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut visitor = VisitorImpl {
writer: f,
result: Ok(()),
first: true,
buffer: B::new(Sealed),
details: PhantomData::<E::Details>,
};
let _: ControlFlow<(), ()> = visitor.visit_many(self.0.clone());
visitor.result
}
}
struct VisitorImpl<W, B, D> {
writer: W,
result: fmt::Result,
first: bool,
buffer: B,
details: PhantomData<D>,
}
impl<W, B, D> Visitor for VisitorImpl<W, B, D>
where
W: fmt::Write,
B: Buffer<Element>,
{
type Details = D;
fn visit(
&mut self,
visitee: &dyn Error<Details = Self::Details>,
ctx: VisitContext<'_, Self::Details>,
) -> ControlFlow<()> {
if self.first {
self.first = false;
} else {
attempt!(writeln!(self.writer), self.result);
}
if ctx.next_sibling().is_none() {
if let Some(x) = self.buffer.last_mut(Sealed) {
x.is_last_sibling = true;
}
}
for depth in 0..=self.buffer.depth(Sealed) {
let Some(x) = self.buffer.get_mut(depth, Sealed).copied() else {
attempt!(
write!(
self.writer,
"{s} LEN exceeded by {c} {h}{p} ",
s = unicode::BLACK_SQUARE,
c = self
.buffer
.depth(Sealed)
.saturating_sub(self.buffer.max_depth(Sealed))
.saturating_add(1),
h = unicode::LIGHT_HORIZONTAL,
p = unicode::BLACK_RIGHT_POINTING_POINTER,
),
self.result
);
break;
};
if depth == self.buffer.depth(Sealed) {
attempt!(
write!(
self.writer,
"{v}{h}{p} ",
v = if x.is_last_sibling {
unicode::LIGHT_ARC_UP_AND_RIGHT
} else {
unicode::LIGHT_VERTICAL_AND_RIGHT
},
h = unicode::LIGHT_HORIZONTAL,
p = unicode::BLACK_RIGHT_POINTING_POINTER,
),
self.result,
);
} else {
attempt!(
write!(
self.writer,
"{v} ",
v = if x.is_last_sibling {
' '
} else {
unicode::LIGHT_VERTICAL
}
),
self.result,
);
}
}
attempt!(write!(self.writer, "{visitee}"), self.result);
ControlFlow::Continue(())
}
fn push(&mut self) -> ControlFlow<()> {
self.buffer.push(Sealed);
ControlFlow::Continue(())
}
fn pop(&mut self) -> ControlFlow<()> {
if let Some(x) = self.buffer.last_mut(Sealed) {
x.is_last_sibling = false;
}
self.buffer.pop(Sealed);
ControlFlow::Continue(())
}
}