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}