1
use std::ffi::OsString;
2
use std::process::ExitCode;
3

            
4
use merc_utilities::MercError;
5

            
6
/// Reports the outcome of a tool's command and yields the process exit code.
7
///
8
/// On success returns [`ExitCode::SUCCESS`]. On failure the error is written to
9
/// stderr and [`ExitCode::FAILURE`] is returned. By default the error is shown
10
/// through its `Display` representation, which is the user-facing message. When
11
/// a backtrace is requested via `RUST_BACKTRACE` (or `RUST_LIB_BACKTRACE`) the
12
/// `Debug` representation is used instead, so developers additionally get the
13
/// underlying error's debug output alongside the backtrace that [`MercError`]
14
/// captures.
15
#[must_use]
16
pub fn report_error(result: Result<(), MercError>) -> ExitCode {
17
    match result {
18
        Ok(()) => ExitCode::SUCCESS,
19
        Err(err) => {
20
            if backtrace_enabled() {
21
                eprintln!("Error: {err:?}");
22
            } else {
23
                eprintln!("Error: {err}");
24
            }
25
            ExitCode::FAILURE
26
        }
27
    }
28
}
29

            
30
/// Returns whether backtrace capture is enabled, mirroring the standard library:
31
/// `RUST_LIB_BACKTRACE` overrides `RUST_BACKTRACE`, and any value other than
32
/// `"0"` enables it.
33
fn backtrace_enabled() -> bool {
34
    backtrace_enabled_from(
35
        std::env::var_os("RUST_LIB_BACKTRACE"),
36
        std::env::var_os("RUST_BACKTRACE"),
37
    )
38
}
39

            
40
/// Pure core of [`backtrace_enabled`], factored out so the precedence rules can
41
/// be tested without mutating process-global environment variables.
42
5
fn backtrace_enabled_from(lib: Option<OsString>, base: Option<OsString>) -> bool {
43
8
    let enabled = |value: Option<OsString>| value.map(|value| value != "0");
44
5
    enabled(lib).or_else(|| enabled(base)).unwrap_or(false)
45
5
}
46

            
47
#[cfg(test)]
48
mod tests {
49
    use std::ffi::OsString;
50

            
51
    use crate::report::backtrace_enabled_from;
52

            
53
    #[test]
54
1
    fn lib_var_overrides_base() {
55
1
        assert!(!backtrace_enabled_from(
56
1
            Some(OsString::from("0")),
57
1
            Some(OsString::from("1"))
58
1
        ));
59
1
        assert!(backtrace_enabled_from(
60
1
            Some(OsString::from("1")),
61
1
            Some(OsString::from("0"))
62
        ));
63
1
    }
64

            
65
    #[test]
66
1
    fn falls_back_to_base_when_lib_unset() {
67
1
        assert!(backtrace_enabled_from(None, Some(OsString::from("1"))));
68
1
        assert!(!backtrace_enabled_from(None, Some(OsString::from("0"))));
69
1
    }
70

            
71
    #[test]
72
1
    fn disabled_when_both_unset() {
73
1
        assert!(!backtrace_enabled_from(None, None));
74
1
    }
75
}