Skip to main content

eska_presentation/
components.rs

1//! Localized presentation of component transport failures.
2
3use crate::localization::{LocalizationValue, Localizer};
4use eska_tool_runtime::Error;
5pub use eska_tool_runtime::contract::respond as tool_contract;
6
7/// Present structured client failures without putting transport diagnostics on stdout.
8#[must_use]
9pub fn present(error: &Error, localizer: &Localizer) -> String {
10    match error {
11        Error::Missing { component } => localizer.format(
12            "component-missing",
13            &[
14                ("tool", LocalizationValue::Text(component.name())),
15                ("variable", LocalizationValue::Text(component.variable())),
16            ],
17        ),
18        Error::Invalid {
19            component,
20            path,
21            source,
22        } => localizer.format(
23            "component-invalid",
24            &[
25                ("tool", LocalizationValue::Text(component.name())),
26                ("path", LocalizationValue::Text(&path.to_string_lossy())),
27                ("reason", LocalizationValue::Text(&source.to_string())),
28            ],
29        ),
30        Error::Incompatible { tool, receipt } => localizer.format(
31            "component-incompatible",
32            &[
33                ("tool", LocalizationValue::Text(tool)),
34                (
35                    "receipt",
36                    LocalizationValue::Text(&receipt.to_string_lossy()),
37                ),
38            ],
39        ),
40        Error::Signal(source) => localizer.format(
41            "component-signal-error",
42            &[("reason", LocalizationValue::Text(&source.to_string()))],
43        ),
44        Error::Process { component, source } => localizer.format(
45            "component-process-error",
46            &[
47                ("tool", LocalizationValue::Text(component.name())),
48                ("reason", LocalizationValue::Text(&source.to_string())),
49            ],
50        ),
51        Error::Selection { component, path } => localizer.format(
52            "component-invalid",
53            &[
54                ("tool", LocalizationValue::Text(component.name())),
55                ("path", LocalizationValue::Text(&path.to_string_lossy())),
56                (
57                    "reason",
58                    LocalizationValue::Text(&localizer.text("component-executable-required")),
59                ),
60            ],
61        ),
62        Error::Timeout => localizer.text("component-timeout"),
63        Error::Cancelled => localizer.text("component-cancelled"),
64        Error::InvalidResponse => localizer.text("component-response-invalid"),
65        Error::OutputTooLarge => localizer.text("component-output-too-large"),
66        Error::Failed => localizer.text("component-failed"),
67        Error::Rejected {
68            component, code, ..
69        } => localizer.format(
70            "component-rejected",
71            &[
72                ("tool", LocalizationValue::Text(component.name())),
73                ("code", LocalizationValue::Text(code)),
74            ],
75        ),
76    }
77}
78
79/// Refuse an incompatible managed installation after help/version parsing, before project writes.
80///
81/// # Errors
82/// Prints the localized installation error and returns the failure status.
83pub fn verify_installation(
84    tool: &str,
85    localizer: &Localizer,
86) -> Result<(), std::process::ExitCode> {
87    let result = std::env::current_exe()
88        .map_err(|_| Error::Failed)
89        .and_then(|binary| eska_tool_runtime::installation::verify(&binary, tool));
90    result.map_err(|error| {
91        eprintln!("{}", present(&error, localizer));
92        std::process::ExitCode::FAILURE
93    })
94}