use core::{fmt, iter, ops::ControlFlow};
use crate::{
Error, ErrorExt as _, VisitContext, Visitor, sealed::SealedVisitor,
};
#[expect(private_bounds)]
pub trait VisitorExt: SealedVisitor + Visitor {
fn visit_many<'a, I, E>(&mut self, errors: I) -> ControlFlow<()>
where
Self: Sized,
I: IntoIterator<Item = &'a E>,
E: Error<Details = Self::Details> + ?Sized + 'a,
{
let mut errors = errors.into_iter().peekable();
while let Some(error) = errors.next() {
let mut ctx = VisitContext::new();
ctx.next_sibling = errors.peek().map(|x| {
let x: &dyn Error<Details = _> = x;
x
});
error.accept(self, ctx)?;
}
ControlFlow::Continue(())
}
fn visit_map_many<'a, I, E, F>(
&mut self,
errors: I,
map_details: F,
) -> ControlFlow<()>
where
Self: Sized,
I: IntoIterator<Item = &'a E>,
E: Error + ?Sized + 'a,
F: Clone + Fn(E::Details) -> Self::Details,
{
let mut errors = errors.into_iter().peekable();
while let Some(error) = errors.next() {
let mut ctx = VisitContext::new();
let error = MapDetails {
error,
map_details: map_details.clone(),
};
if let Some(&next_sibling) = errors.peek() {
let next_sibling: &dyn Error<Details = _> = &MapDetails {
error: next_sibling,
map_details: map_details.clone(),
};
ctx.next_sibling = Some(next_sibling);
error.accept(self, ctx)?;
} else {
error.accept(self, ctx)?;
}
}
ControlFlow::Continue(())
}
fn visit_children_of<E>(&mut self, error: &E) -> ControlFlow<()>
where
E: Error<Details = Self::Details> + ?Sized,
{
let mut visitor = SkipRoots {
inner: self,
depth: 0,
};
visitor.visit_many(iter::once(error))?;
ControlFlow::Continue(())
}
fn visit_map_children_of<E, F>(
&mut self,
error: &E,
map_details: F,
) -> ControlFlow<()>
where
E: Error + ?Sized,
F: Clone + Fn(E::Details) -> Self::Details,
{
let mut visitor = SkipRoots {
inner: self,
depth: 0,
};
visitor.visit_map_many(iter::once(error), map_details)?;
ControlFlow::Continue(())
}
}
impl<V> VisitorExt for V where V: Visitor {}
struct SkipRoots<V> {
inner: V,
depth: usize,
}
impl<V> Visitor for SkipRoots<V>
where
V: Visitor,
{
type Details = V::Details;
fn visit(
&mut self,
visitee: &dyn Error<Details = V::Details>,
ctx: VisitContext<'_, V::Details>,
) -> ControlFlow<()> {
if self.depth > 0 {
self.inner.visit(visitee, ctx)?;
}
ControlFlow::Continue(())
}
fn push(&mut self) -> ControlFlow<()> {
if self.depth > 0 {
self.inner.push()?;
}
self.depth =
self.depth.checked_add(1).expect("depth should not overflow");
ControlFlow::Continue(())
}
fn pop(&mut self) -> ControlFlow<()> {
self.depth =
self.depth.checked_sub(1).expect("depth should not underflow");
if self.depth > 0 {
self.inner.pop()?;
}
ControlFlow::Continue(())
}
}
struct MapDetails<'a, E, F>
where
E: ?Sized,
{
error: &'a E,
map_details: F,
}
impl<E, F> fmt::Display for MapDetails<'_, E, F>
where
E: fmt::Display + ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.error, f)
}
}
impl<E, F> fmt::Debug for MapDetails<'_, E, F>
where
E: fmt::Debug + ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MapDetails")
.field("error", &self.error)
.finish_non_exhaustive()
}
}
impl<E, F, D> Error for MapDetails<'_, E, F>
where
E: Error + ?Sized,
F: Clone + Fn(E::Details) -> D,
{
type Details = D;
fn accept(
&self,
mut visitor: &mut dyn Visitor<Details = Self::Details>,
ctx: VisitContext<'_, Self::Details>,
) -> ControlFlow<()> {
visitor.visit(self, ctx)?;
if self.error.has_children() {
visitor.push()?;
visitor
.visit_map_children_of(self.error, self.map_details.clone())?;
visitor.pop()?;
}
ControlFlow::Continue(())
}
fn details(&self) -> Self::Details {
(self.map_details)(self.error.details())
}
}