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