Skip to main content

gix_error/
test.rs

1use crate::Error;
2
3/// An error for test functions that accepts any error supported by a boxed standard error.
4///
5/// Unlike [`Error`], this type deliberately does not implement [`std::error::Error`]. This allows it to accept arbitrary
6/// errors via [`From`] without conflicting with the standard library's identity conversion.
7/// Its [`Debug`](std::fmt::Debug) output includes the complete diagnostic tree or chain, omitting classification markers
8/// unless only markers are available. Custom I/O wrappers show their kind, with their payloads reported separately.
9/// Captured caller locations are included unless alternate formatting is used.
10pub struct TestError(Error);
11
12/// A result type for test functions whose errors are reported through [`TestError`]'s complete diagnostics.
13pub type TestResult<T = ()> = std::result::Result<T, TestError>;
14
15impl<E> From<E> for TestError
16where
17    E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
18{
19    #[track_caller]
20    fn from(error: E) -> Self {
21        let error = error.into();
22        TestError(match error.downcast::<Error>() {
23            Ok(error) => *error,
24            Err(error) => Error::from_boxed(error),
25        })
26    }
27}
28
29impl From<TestError> for Error {
30    fn from(error: TestError) -> Self {
31        error.0
32    }
33}
34
35impl std::fmt::Debug for TestError {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        #[cfg(any(feature = "tree-error", not(feature = "auto-chain-error")))]
38        return std::fmt::Debug::fmt(self.0.inner.frame(), f);
39
40        #[cfg(all(feature = "auto-chain-error", not(feature = "tree-error")))]
41        {
42            let write_error =
43                |error: crate::error::DisplaySource<'_>, f: &mut std::fmt::Formatter<'_>| -> std::fmt::Result {
44                    crate::exn::impls::ErrorMode::Display.fmt(error.error(), f)?;
45                    if !f.alternate()
46                        && let Some(location) = error.location()
47                    {
48                        crate::write_location(f, location)?;
49                    }
50                    Ok(())
51                };
52            let mut errors = self
53                .0
54                .iter_errors_with_locations()
55                // Boundary contents are emitted separately by the iterator.
56                .filter(|source| !source.error().is::<Error>())
57                .peekable();
58            let Some(error) = errors.next() else {
59                return std::fmt::Display::fmt(&self.0.inner, f);
60            };
61            write_error(error, f)?;
62            if errors.peek().is_some() {
63                write!(f, "\n\nCaused by:")?;
64                for (index, error) in errors.enumerate() {
65                    write!(f, "\n    {index}: ")?;
66                    write_error(error, f)?;
67                }
68            }
69            Ok(())
70        }
71    }
72}