foundry_cli/
handler.rs

1use eyre::EyreHandler;
2use itertools::Itertools;
3use std::{error::Error, fmt};
4
5/// A custom context type for Foundry specific error reporting via `eyre`.
6#[derive(Default)]
7pub struct Handler {
8    debug_handler: Option<Box<dyn EyreHandler>>,
9}
10
11impl Handler {
12    /// Create a new instance of the `Handler`.
13    pub fn new() -> Self {
14        Self::default()
15    }
16
17    /// Override the debug handler with a custom one.
18    pub fn debug_handler(mut self, debug_handler: Option<Box<dyn EyreHandler>>) -> Self {
19        self.debug_handler = debug_handler;
20        self
21    }
22}
23
24impl EyreHandler for Handler {
25    fn display(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        use fmt::Display;
27        foundry_common::errors::dedup_chain(error).into_iter().format("; ").fmt(f)
28    }
29
30    fn debug(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        if let Some(debug_handler) = &self.debug_handler {
32            return debug_handler.debug(error, f);
33        }
34
35        if f.alternate() {
36            return fmt::Debug::fmt(error, f);
37        }
38        let errors = foundry_common::errors::dedup_chain(error);
39
40        let (error, sources) = errors.split_first().unwrap();
41        write!(f, "{error}")?;
42
43        if !sources.is_empty() {
44            write!(f, "\n\nContext:")?;
45
46            let multiple = sources.len() > 1;
47            for (n, error) in sources.iter().enumerate() {
48                writeln!(f)?;
49                if multiple {
50                    write!(f, "- Error #{n}: {error}")?;
51                } else {
52                    write!(f, "- {error}")?;
53                }
54            }
55        }
56
57        Ok(())
58    }
59
60    fn track_caller(&mut self, location: &'static std::panic::Location<'static>) {
61        if let Some(debug_handler) = &mut self.debug_handler {
62            debug_handler.track_caller(location);
63        }
64    }
65}
66
67/// Installs the Foundry [`eyre`] and [`panic`](mod@std::panic) hooks as the global ones.
68///
69/// # Details
70///
71/// By default a simple user-centric handler is installed, unless
72/// `FOUNDRY_DEBUG` is set in the environment, in which case a more
73/// verbose debug-centric handler is installed.
74///
75/// Panics are always caught by the more debug-centric handler.
76pub fn install() {
77    if std::env::var_os("RUST_BACKTRACE").is_none() {
78        unsafe {
79            std::env::set_var("RUST_BACKTRACE", "1");
80        }
81    }
82
83    let panic_section =
84        "This is a bug. Consider reporting it at https://github.com/foundry-rs/foundry";
85    let (panic_hook, debug_hook) =
86        color_eyre::config::HookBuilder::default().panic_section(panic_section).into_hooks();
87    panic_hook.install();
88    let debug_hook = debug_hook.into_eyre_hook();
89    let debug = std::env::var_os("FOUNDRY_DEBUG").is_some();
90    if let Err(e) = eyre::set_hook(Box::new(move |e| {
91        Box::new(Handler::new().debug_handler(debug.then(|| debug_hook(e))))
92    })) {
93        debug!("failed to install eyre error hook: {e}");
94    }
95}