heron_rebuild_workflow/
error.rs1use anyhow::Result;
2use colored::Colorize;
3
4use crate::WorkflowStrings;
5
6#[derive(Debug, thiserror::Error)]
8#[error("{0} failed due to {1} errors")]
9pub struct AggregatedErrors(pub String, pub usize);
10
11pub trait Recap: std::fmt::Debug + Send + Sync {
13 fn recap(&self, wf: &WorkflowStrings) -> Result<Option<String>>;
14}
15
16#[derive(Debug, thiserror::Error)]
19#[error("{e:?}")]
20pub struct Recapper {
21 e: Box<dyn Recap>,
22}
23
24impl Recapper {
25 pub fn new(e: impl Recap + 'static) -> Self {
26 Self { e: Box::new(e) }
27 }
28}
29
30pub struct Errors {
32 errors: Vec<anyhow::Error>,
33}
34
35impl Default for Errors {
36 fn default() -> Self {
37 Self {
38 errors: Vec::with_capacity(0),
41 }
42 }
43}
44
45impl Errors {
46 pub fn add_context(&mut self, e: anyhow::Error, msg: String) {
47 log::trace!("{msg}: {e:?}");
48 self.errors.push(e.context(msg));
49 }
50
51 pub fn add(&mut self, e: anyhow::Error) {
52 log::trace!("error: {e:?}");
53 self.errors.push(e);
54 }
55
56 pub fn print_recap(&self, label: &str, wf: &WorkflowStrings) -> Result<()> {
59 if self.errors.is_empty() {
60 Ok(())
61 } else {
62 eprintln!("\n{} {}:\n", "Encountered errors while".red(), label.red());
63 for e in &self.errors {
64 use anyhow::Context;
65 recap(e, wf).context("Unable to print error list due to errors while printing")?;
66 }
67 Err(AggregatedErrors(label.to_owned(), self.errors.len()).into())
68 }
69 }
70}
71
72fn recap(e: &anyhow::Error, wf: &WorkflowStrings) -> Result<()> {
73 eprint!("{}: ", "ERROR".red());
74
75 handle_recapper_anyhow(e, wf)?;
76 for cause in e.chain().skip(1) {
77 eprint!("\nCaused by:\n\t");
78 handle_recapper_dyn(cause, wf)?;
79 }
80 eprintln!();
81 Ok(())
82}
83
84fn handle_recapper_dyn(e: &(dyn std::error::Error + 'static), wf: &WorkflowStrings) -> Result<()> {
87 if let Some(recapper) = e.downcast_ref::<Recapper>() {
88 if let Some(msg) = recapper.e.recap(wf)? {
89 eprintln!("{}", msg);
90 return Ok(());
91 }
92 }
93 eprintln!("{}", e);
94 Ok(())
95}
96
97fn handle_recapper_anyhow(e: &anyhow::Error, wf: &WorkflowStrings) -> Result<()> {
98 if let Some(recapper) = e.downcast_ref::<Recapper>() {
99 if let Some(msg) = recapper.e.recap(wf)? {
100 eprintln!("{}", msg);
101 return Ok(());
102 }
103 }
104 eprintln!("{}", e);
105 Ok(())
106}