pmpx_engine/error.rs
1//! Why a command could not be started.
2//!
3//! Two outcomes, because the next step differs: "there is no such program" is a setup problem the
4//! user can fix (and the message lists the near misses), while "it is there and would not start" is
5//! something to report with its own cause. Neither is this crate's decision to print.
6
7use std::ffi::{OsStr, OsString};
8use std::fmt;
9
10/// A program that could not be started.
11#[derive(Debug)]
12pub enum EngineError {
13 /// The plugin store refused -- installing, removing, or looking something up -- and the kit's own
14 /// message is the whole explanation; this crate has nothing to add to it.
15 ///
16 /// Behind the `store` feature, because the kit is an optional dependency: a host that only runs
17 /// plugins does not pay for `ureq`, `rustls` and `ring`, and neither does its error type.
18 #[cfg(feature = "store")]
19 Kit(crate_plugin_kit::KitError),
20
21 /// The program was not found: either the path a plugin pointed at does not exist, or no name on
22 /// `PATH` matches.
23 ///
24 /// The message is built here rather than by the caller because only this crate knows what was
25 /// searched, and it is the part a person needs to see.
26 NotFound(String),
27
28 /// The user asked for something impossible (`-C` at a directory that is not there).
29 Usage(String),
30
31 /// The setup could not be completed: the store, the manifest list, the project root.
32 Setup(String),
33
34 /// No project type was detected, or the pinned plugin cannot answer. Exit code 3.
35 NoProject(String),
36
37 /// The decision refused to choose, with the reason the decision crate reported.
38 Detect(pmpx_detect::DetectFailure),
39
40 /// The plugin answered that it cannot do this.
41 Call {
42 /// The plugin's name.
43 plugin: String,
44 /// The verb it refused.
45 verb: String,
46 /// What it said.
47 error: pmpx_loader::CallError,
48 },
49
50 /// It was found, and starting it failed anyway.
51 Start {
52 /// The program, as it was about to be started.
53 program: OsString,
54 /// What the operating system said.
55 source: std::io::Error,
56 },
57}
58
59impl EngineError {
60 /// Refuse a program, with the message a person needs.
61 pub(crate) fn not_found(message: String) -> Self {
62 Self::NotFound(message)
63 }
64
65 /// The full explanation, for whoever is showing it.
66 pub fn message(&self) -> String {
67 match self {
68 #[cfg(feature = "store")]
69 Self::Kit(error) => error.to_string(),
70 Self::NotFound(message) | Self::Usage(message) | Self::Setup(message) => {
71 message.clone()
72 }
73 Self::NoProject(message) => message.clone(),
74 Self::Detect(failure) => failure.message(),
75 Self::Call {
76 plugin,
77 verb,
78 error,
79 } => format!("plugin {plugin} cannot do `{verb}`: {error}"),
80 Self::Start { program, source } => {
81 format!("failed to start {}: {source}", program.to_string_lossy())
82 }
83 }
84 }
85
86 /// The program this is about, when there is one.
87 pub fn program(&self) -> Option<&OsStr> {
88 match self {
89 Self::Start { program, .. } => Some(program),
90 _ => None,
91 }
92 }
93
94 /// Whether this is "there is no such program", which is a setup problem rather than a failure.
95 pub fn is_not_found(&self) -> bool {
96 matches!(
97 self,
98 Self::NotFound(_) | Self::NoProject(_) | Self::Setup(_)
99 )
100 }
101}
102
103impl fmt::Display for EngineError {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 f.write_str(&self.message())
106 }
107}
108
109impl std::error::Error for EngineError {
110 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
111 match self {
112 Self::Start { source, .. } => Some(source),
113 #[cfg(feature = "store")]
114 Self::Kit(error) => Some(error),
115 _ => None,
116 }
117 }
118}
119
120#[cfg(feature = "store")]
121impl From<crate_plugin_kit::KitError> for EngineError {
122 fn from(error: crate_plugin_kit::KitError) -> Self {
123 Self::Kit(error)
124 }
125}
126
127/// The crate's own result, so the ported helpers did not have to change shape.
128pub(crate) type Result<T> = std::result::Result<T, EngineError>;