foundry_common/errors/
mod.rs

1//! Commonly used errors
2
3mod fs;
4pub use fs::FsPathError;
5
6mod artifacts;
7pub use artifacts::*;
8
9mod private {
10    use eyre::Chain;
11    use std::error::Error;
12
13    pub trait ErrorChain {
14        fn chain(&self) -> Chain<'_>;
15    }
16
17    impl ErrorChain for dyn Error + 'static {
18        fn chain(&self) -> Chain<'_> {
19            Chain::new(self)
20        }
21    }
22
23    impl ErrorChain for eyre::Report {
24        fn chain(&self) -> Chain<'_> {
25            self.chain()
26        }
27    }
28}
29
30/// Displays a chain of errors in a single line.
31pub fn display_chain<E: private::ErrorChain + ?Sized>(error: &E) -> String {
32    dedup_chain(error).join("; ")
33}
34
35/// Deduplicates a chain of errors.
36pub fn dedup_chain<E: private::ErrorChain + ?Sized>(error: &E) -> Vec<String> {
37    let mut causes = all_sources(error);
38    // Deduplicate the common pattern `msg1: msg2; msg2` -> `msg1: msg2`.
39    causes.dedup_by(|b, a| a.contains(b.as_str()));
40    causes
41}
42
43fn all_sources<E: private::ErrorChain + ?Sized>(err: &E) -> Vec<String> {
44    err.chain().map(|cause| cause.to_string().trim().to_string()).collect()
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn dedups_contained() {
53        #[derive(thiserror::Error, Debug)]
54        #[error("my error: {0}")]
55        struct A(#[from] B);
56
57        #[derive(thiserror::Error, Debug)]
58        #[error("{0}")]
59        struct B(String);
60
61        let ee = eyre::Report::from(A(B("hello".into())));
62        assert_eq!(ee.chain().count(), 2, "{ee:?}");
63        let full = all_sources(&ee).join("; ");
64        assert_eq!(full, "my error: hello; hello");
65        let chained = display_chain(&ee);
66        assert_eq!(chained, "my error: hello");
67    }
68}