1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
use std::error::Error;

use eyre::EyreHandler;
use yansi::Paint;

/// A custom context type for Trustblock specific error reporting via `eyre`
#[derive(Debug)]
pub struct Handler;

impl EyreHandler for Handler {
    fn debug(
        &self,
        error: &(dyn Error + 'static),
        f: &mut core::fmt::Formatter<'_>,
    ) -> core::fmt::Result {
        if f.alternate() {
            return core::fmt::Debug::fmt(error, f);
        }
        writeln!(f)?;
        write!(f, "{}", Paint::red(error))?;

        if let Some(cause) = error.source() {
            write!(f, "\n\nContext:")?;

            let multiple = cause.source().is_some();
            let errors = std::iter::successors(Some(cause), |e| (*e).source());

            for (n, error) in errors.enumerate() {
                writeln!(f)?;
                if multiple {
                    write!(f, "- Error #{n}: {error}")?;
                } else {
                    write!(f, "- {error}")?;
                }
            }
        }

        Ok(())
    }
}

/// Installs the Trustblock eyre hook as the global error report hook.
///
/// # Details
///
/// By default a simple user-centric handler is installed, unless
/// `TRUSTBLOCK_DEBUG` is set in the environment, in which case a more
/// verbose debug-centric handler is installed.
///
/// Panics are always caught by the more debug-centric handler.
pub fn install() -> eyre::Result<()> {
    let debug_enabled = std::env::var("TRUSTBLOCK_DEBUG").is_ok();

    if debug_enabled {
        color_eyre::install()?;
    } else {
        let (panic_hook, _) = color_eyre::config::HookBuilder
            ::default()
            .panic_section(
                "This is a bug. Consider reporting it at https://github.com/Trustblock-Inc/trustblock-cli"
            )
            .into_hooks();
        panic_hook.install();

        eyre::set_hook(Box::new(move |_| Box::new(Handler)))?;
    }

    Ok(())
}