derail 0.3.0

An alternative to `core::error::Error`.
Documentation
use std::{borrow::Cow, ops::ControlFlow};

use derail::{Error, VisitContext, Visitor};

#[cfg(feature = "alloc")]
mod impls_alloc;
mod impls_core;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum Event<'a, D> {
    Visit {
        message: Cow<'a, str>,
        next_sibling_message: Option<Cow<'a, str>>,
        details: D,
    },
    Push,
    Pop,
}

impl<'a, D> Event<'a, D> {
    pub(crate) fn visit<S1, S2>(
        message: S1,
        details: D,
        next_sibling_message: Option<S2>,
    ) -> Self
    where
        S1: Into<Cow<'a, str>>,
        S2: Into<Cow<'a, str>>,
    {
        Self::Visit {
            message: message.into(),
            details,
            next_sibling_message: next_sibling_message.map(Into::into),
        }
    }
}

pub(crate) struct TestVisitor<D> {
    pub(crate) events: Vec<Event<'static, D>>,
    pub(crate) break_on_ascend: bool,
}

impl<D> TestVisitor<D> {
    pub(crate) fn new() -> Self {
        Self {
            break_on_ascend: false,
            events: Vec::new(),
        }
    }
}

impl<D> Visitor for TestVisitor<D> {
    type Details = D;

    fn visit(
        &mut self,
        visitee: &dyn Error<Details = Self::Details>,
        ctx: VisitContext<'_, Self::Details>,
    ) -> ControlFlow<()> {
        self.events.push(Event::visit(
            visitee.to_string(),
            visitee.details(),
            ctx.next_sibling().map(ToString::to_string),
        ));

        ControlFlow::Continue(())
    }

    fn push(&mut self) -> ControlFlow<()> {
        self.events.push(Event::Push);

        ControlFlow::Continue(())
    }

    fn pop(&mut self) -> ControlFlow<()> {
        if self.break_on_ascend {
            return ControlFlow::Break(());
        }

        self.events.push(Event::Pop);

        ControlFlow::Continue(())
    }
}

#[test]
fn dyn_safe() {
    #[expect(dead_code)]
    fn inner(_: &dyn Visitor<Details = ()>) {}
}