foundry_common/errors/
mod.rs1mod fs;
4pub use fs::FsPathError;
5
6mod private {
7 use eyre::Chain;
8 use std::error::Error;
9
10 pub trait ErrorChain {
11 fn chain(&self) -> Chain<'_>;
12 }
13
14 impl ErrorChain for dyn Error + 'static {
15 fn chain(&self) -> Chain<'_> {
16 Chain::new(self)
17 }
18 }
19
20 impl ErrorChain for eyre::Report {
21 fn chain(&self) -> Chain<'_> {
22 self.chain()
23 }
24 }
25}
26
27pub fn display_chain<E: private::ErrorChain + ?Sized>(error: &E) -> String {
29 dedup_chain(error).join("; ")
30}
31
32pub fn dedup_chain<E: private::ErrorChain + ?Sized>(error: &E) -> Vec<String> {
34 let mut causes = all_sources(error);
35 causes.dedup_by(|b, a| a.contains(b.as_str()));
37 causes
38}
39
40fn all_sources<E: private::ErrorChain + ?Sized>(err: &E) -> Vec<String> {
41 err.chain().map(|cause| cause.to_string().trim().to_string()).collect()
42}
43
44pub fn convert_solar_errors(dcx: &solar::interface::diagnostics::DiagCtxt) -> eyre::Result<()> {
46 match dcx.emitted_errors() {
47 Some(Ok(())) => Ok(()),
48 Some(Err(e)) if !e.is_empty() => {
49 eyre::bail!("solar reported errors:\n\n{e}");
50 }
51 _ if dcx.has_errors().is_err() => {
52 let n = dcx.err_count();
54 let plural = if n == 1 { "" } else { "s" };
55 eyre::bail!(
56 "solar reported {n} error{plural}; see the diagnostic{plural} printed above"
57 );
58 }
59 _ => Ok(()),
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use solar::interface::diagnostics::{DiagCtxt, SilentEmitter};
67
68 #[test]
69 fn dedups_contained() {
70 #[derive(thiserror::Error, Debug)]
71 #[error("my error: {0}")]
72 struct A(#[from] B);
73
74 #[derive(thiserror::Error, Debug)]
75 #[error("{0}")]
76 struct B(String);
77
78 let ee = eyre::Report::from(A(B("hello".into())));
79 assert_eq!(ee.chain().count(), 2, "{ee:?}");
80 let full = all_sources(&ee).join("; ");
81 assert_eq!(full, "my error: hello; hello");
82 let chained = display_chain(&ee);
83 assert_eq!(chained, "my error: hello");
84 }
85
86 #[test]
93 fn solar_non_buffer_emitter_singular() {
94 let dcx = DiagCtxt::new(Box::new(SilentEmitter::new_silent()));
95 dcx.err("boom").emit();
96
97 let err = convert_solar_errors(&dcx).unwrap_err();
98 assert_eq!(err.to_string(), "solar reported 1 error; see the diagnostic printed above");
99 }
100
101 #[test]
102 fn solar_non_buffer_emitter_plural() {
103 let dcx = DiagCtxt::new(Box::new(SilentEmitter::new_silent()));
104 dcx.err("boom 1").emit();
105 dcx.err("boom 2").emit();
106
107 let err = convert_solar_errors(&dcx).unwrap_err();
108 assert_eq!(err.to_string(), "solar reported 2 errors; see the diagnostics printed above");
109 }
110
111 #[test]
112 fn solar_no_errors_is_ok() {
113 let dcx = DiagCtxt::new(Box::new(SilentEmitter::new_silent()));
114 assert!(convert_solar_errors(&dcx).is_ok());
115 }
116}