derail 0.3.0

An alternative to `core::error::Error`.
Documentation
//! [`VisitorExt`] and its implementation details.

use core::{fmt, iter, ops::ControlFlow};

use crate::{
    Error, ErrorExt as _, VisitContext, Visitor, sealed::SealedVisitor,
};

/// Extension trait that provides more methods for [`Visitor`]s.
#[expect(private_bounds)]
pub trait VisitorExt: SealedVisitor + Visitor {
    /// Visit [`Error`]s and their children recursively, depth-first.
    ///
    /// This method calls [`Error::accept`] for each item in `errors`, passing
    /// `self` to its `visitor` parameter.
    ///
    /// To visit a single [`Error`], make use of [`core::iter::once`].
    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(())
    }

    /// [`visit_many`] with a function to convert from
    /// [`E::Details`](Error::Details) to [`Self::Details`](Visitor::Details).
    ///
    /// This is primarily useful for enabling an [`Error`] to contain child
    /// [`Error`]s where the parent and children have different concrete types
    /// for [`Error::Details`].
    ///
    /// `map_details` is only called if/when `Self` calls [`Error::details`] on
    /// any [`Error`] during the visit.
    ///
    /// Where `map_details` would be given an identity function, use
    /// [`visit_many`] instead.
    ///
    /// [`visit_many`]: VisitorExt::visit_many
    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(())
    }

    /// Visit an [`Error`]'s children recursively, depth-first.
    ///
    /// This method will not visit `error` itself.
    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(())
    }

    /// [`visit_children_of`] with a function to convert from
    /// [`E::Details`](Error::Details) to [`Self::Details`](Visitor::Details).
    ///
    /// This is primarily useful for enabling an [`Error`] to contain child
    /// [`Error`]s where the parent and children have different concrete types
    /// for [`Error::Details`].
    ///
    /// `map_details` is only called if/when `Self` calls [`Error::details`] on
    /// any [`Error`] during the visit.
    ///
    /// Where `map_details` would be given an identity function, use
    /// [`visit_children_of`] instead.
    ///
    /// [`visit_children_of`]: VisitorExt::visit_children_of
    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 {}

/// [`Visitor`] for [`VisitorExt::visit_children_of`].
struct SkipRoots<V> {
    /// The inner [`Visitor`] whose methods to call after skipping the first
    /// layer of nodes.
    inner: V,

    /// Tracks the current depth into the tree.
    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(())
    }
}

/// Wrapper type to perform a conversion on an [`Error`]'s details.
struct MapDetails<'a, E, F>
where
    E: ?Sized,
{
    /// The wrapped [`Error`].
    error: &'a E,

    /// The conversion function.
    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())
    }
}