use core::error::Error as CoreError;
use std::{fmt, iter};
use derail::{CoreCompat, Error, VisitorExt as _};
use derail_macros::Error;
use similar_asserts::assert_eq;
use crate::visitor::{Event, TestVisitor};
#[cfg(feature = "alloc")]
mod impls_alloc;
mod impls_core;
#[derive(Debug, Clone, Error)]
#[derail(
type Details = D,
display("{_0}"),
impl Error where { D: fmt::Debug + Clone },
)]
pub(crate) struct Leaf<D>(
pub(crate) &'static str,
#[derail(details)] pub(crate) D,
);
#[derive(Debug, Clone, Error)]
#[derail(
type Details = D,
display("{_0}"),
impl Error where {
E: Error<Details = D>,
D: fmt::Debug + Clone,
},
)]
pub(crate) struct Tree<E, D>(
pub(crate) &'static str,
#[derail(details)] pub(crate) D,
#[derail(children)] pub(crate) Vec<E>,
);
#[test]
fn dyn_safe() {
#[expect(dead_code)]
fn inner(_: &dyn Error<Details = ()>) {}
}
fn make_one_many_many() -> impl Error<Details = i32> {
let list_0_0 = Leaf("leaf_0_0", 2);
let list_0_1 = Leaf("leaf_0_1", 3);
let list_0_2 = Leaf("leaf_0_2", 4);
let list_0 = Tree("tree_0", 1, vec![list_0_0, list_0_1, list_0_2]);
let list_1_0 = Leaf("leaf_1_0", 6);
let list_1_1 = Leaf("leaf_1_1", 7);
let list_1_2 = Leaf("leaf_1_2", 8);
let list_1 = Tree("tree_1", 5, vec![list_1_0, list_1_1, list_1_2]);
let list_2_0 = Leaf("leaf_2_0", 10);
let list_2_1 = Leaf("leaf_2_1", 11);
let list_2_2 = Leaf("leaf_2_2", 12);
let list_2 = Tree("tree_2", 9, vec![list_2_0, list_2_1, list_2_2]);
Tree("tree", 0, vec![list_0, list_1, list_2])
}
#[test]
fn one_many_many() {
let expected = [
Event::visit("tree", 0, None::<&str>),
Event::Push,
Event::visit("tree_0", 1, Some("tree_1")),
Event::Push,
Event::visit("leaf_0_0", 2, Some("leaf_0_1")),
Event::visit("leaf_0_1", 3, Some("leaf_0_2")),
Event::visit("leaf_0_2", 4, None::<&str>),
Event::Pop,
Event::visit("tree_1", 5, Some("tree_2")),
Event::Push,
Event::visit("leaf_1_0", 6, Some("leaf_1_1")),
Event::visit("leaf_1_1", 7, Some("leaf_1_2")),
Event::visit("leaf_1_2", 8, None::<&str>),
Event::Pop,
Event::visit("tree_2", 9, None::<&str>),
Event::Push,
Event::visit("leaf_2_0", 10, Some("leaf_2_1")),
Event::visit("leaf_2_1", 11, Some("leaf_2_2")),
Event::visit("leaf_2_2", 12, None::<&str>),
Event::Pop,
Event::Pop,
];
let error = make_one_many_many();
let mut visitor = TestVisitor::new();
visitor
.visit_many(iter::once(&error))
.continue_value()
.expect("visit should complete");
assert_eq!(
expected: expected,
actual: visitor.events.as_slice(),
);
}
#[test]
fn one_many_many_no_ascending() {
let expected = [
Event::visit("tree", 0, None::<&str>),
Event::Push,
Event::visit("tree_0", 1, Some("tree_1")),
Event::Push,
Event::visit("leaf_0_0", 2, Some("leaf_0_1")),
Event::visit("leaf_0_1", 3, Some("leaf_0_2")),
Event::visit("leaf_0_2", 4, None::<&str>),
];
let error = make_one_many_many();
let mut visitor = TestVisitor::new();
visitor.break_on_ascend = true;
visitor
.visit_many(iter::once(&error))
.break_value()
.expect("visit should not complete");
assert_eq!(
expected: expected,
actual: visitor.events.as_slice(),
);
}
#[test]
fn one_many_many_children() {
let expected = [
Event::visit("tree_0", 1, Some("tree_1")),
Event::Push,
Event::visit("leaf_0_0", 2, Some("leaf_0_1")),
Event::visit("leaf_0_1", 3, Some("leaf_0_2")),
Event::visit("leaf_0_2", 4, None::<&str>),
Event::Pop,
Event::visit("tree_1", 5, Some("tree_2")),
Event::Push,
Event::visit("leaf_1_0", 6, Some("leaf_1_1")),
Event::visit("leaf_1_1", 7, Some("leaf_1_2")),
Event::visit("leaf_1_2", 8, None::<&str>),
Event::Pop,
Event::visit("tree_2", 9, None::<&str>),
Event::Push,
Event::visit("leaf_2_0", 10, Some("leaf_2_1")),
Event::visit("leaf_2_1", 11, Some("leaf_2_2")),
Event::visit("leaf_2_2", 12, None::<&str>),
Event::Pop,
];
let error = make_one_many_many();
let mut visitor = TestVisitor::new();
visitor
.visit_children_of(&error)
.continue_value()
.expect("visit should complete");
assert_eq!(
expected: expected,
actual: visitor.events.as_slice(),
);
}
fn make_many_one() -> Vec<impl Error<Details = i32>> {
let leaf_0 = Leaf("leaf_0", 1);
let leaf_1 = Leaf("leaf_1", 3);
let leaf_2 = Leaf("leaf_2", 5);
let tree_0 = Tree("tree_0", 0, vec![leaf_0]);
let tree_1 = Tree("tree_1", 2, vec![leaf_1]);
let tree_2 = Tree("tree_2", 4, vec![leaf_2]);
vec![tree_0, tree_1, tree_2]
}
#[test]
fn many_one() {
let expected = [
Event::visit("tree_0", 0, Some("tree_1")),
Event::Push,
Event::visit("leaf_0", 1, None::<&str>),
Event::Pop,
Event::visit("tree_1", 2, Some("tree_2")),
Event::Push,
Event::visit("leaf_1", 3, None::<&str>),
Event::Pop,
Event::visit("tree_2", 4, None::<&str>),
Event::Push,
Event::visit("leaf_2", 5, None::<&str>),
Event::Pop,
];
let errors = make_many_one();
let mut visitor = TestVisitor::new();
visitor
.visit_many(&errors)
.continue_value()
.expect("visit should complete");
assert_eq!(
expected: expected,
actual: visitor.events.as_slice(),
);
}
macro_rules! core_error {
($name:ident, $msg:literal $(,)?) => {
core_error!(@ $name, $msg, None);
};
($name:ident, $msg:literal, $source:expr $(,)?) => {
core_error!(@ $name, $msg, Some($source));
};
(@ $name:ident, $msg:literal, $source:expr $(,)?) => {
#[derive(Debug)]
struct $name;
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, $msg)
}
}
impl CoreError for $name {
fn source(&self) -> Option<&(dyn CoreError + 'static)> {
$source
}
}
};
}
core_error!(
EncabulatorMalfunction,
"encabulator malfunctioned",
&MarzelvaneStuck,
);
core_error!(MarzelvaneStuck, "marzelvane stuck", &WaneshaftDiscombobulated);
core_error!(WaneshaftDiscombobulated, "waneshaft discombobulated");
#[test]
fn from_core() {
let expected = [
Event::visit("encabulator malfunctioned", (), None::<&str>),
Event::Push,
Event::visit("marzelvane stuck", (), None::<&str>),
Event::Push,
Event::visit("waneshaft discombobulated", (), None::<&str>),
Event::Pop,
Event::Pop,
];
let error = CoreCompat::from(EncabulatorMalfunction);
let mut visitor = TestVisitor::new();
visitor
.visit_many(iter::once(&error))
.continue_value()
.expect("visit should complete");
assert_eq!(
expected: expected,
actual: visitor.events.as_slice(),
);
}