Skip to main content

foundry_cli/
handler.rs

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