command_error/
exec_error.rs

1use std::fmt::Debug;
2use std::fmt::Display;
3
4use crate::CommandDisplay;
5#[cfg(doc)]
6use crate::CommandExt;
7#[cfg(doc)]
8use crate::OutputError;
9#[cfg(feature = "miette")]
10use miette::Diagnostic;
11
12/// An error from failing to execute a command. Produced by [`CommandExt`].
13///
14/// This is a command that fails to start, rather than a command that exits with a non-zero status
15/// or similar, like [`OutputError`].
16///
17/// ```
18/// # use pretty_assertions::assert_eq;
19/// # use std::process::Command;
20/// # use command_error::Utf8ProgramAndArgs;
21/// # use command_error::CommandDisplay;
22/// # use command_error::ExecError;
23/// let mut command = Command::new("echo");
24/// command.arg("puppy doggy");
25/// let displayed: Utf8ProgramAndArgs = (&command).into();
26/// let error = ExecError::new(
27///     Box::new(displayed),
28///     std::io::Error::new(
29///         std::io::ErrorKind::NotFound,
30///         "File not found (os error 2)"
31///     ),
32/// );
33/// assert_eq!(
34///     error.to_string(),
35///     "Failed to execute `echo`: File not found (os error 2)"
36/// );
37/// ```
38pub struct ExecError {
39    pub(crate) command: Box<dyn CommandDisplay + Send + Sync>,
40    pub(crate) inner: std::io::Error,
41}
42
43impl ExecError {
44    /// Construct a new [`ExecError`].
45    pub fn new(command: Box<dyn CommandDisplay + Send + Sync>, inner: std::io::Error) -> Self {
46        Self { command, inner }
47    }
48}
49
50impl Debug for ExecError {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        f.debug_struct("ExecError")
53            .field("program", &self.command.program())
54            .field("inner", &self.inner)
55            .finish()
56    }
57}
58
59impl Display for ExecError {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        write!(
62            f,
63            "Failed to execute `{}`: {}",
64            self.command.program_quoted(),
65            self.inner
66        )
67    }
68}
69
70impl std::error::Error for ExecError {}
71
72#[cfg(feature = "miette")]
73impl Diagnostic for ExecError {
74    fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
75        Some(Box::new(format!(
76            "Is {} installed and present on your $PATH?",
77            self.command.program_quoted()
78        )))
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use static_assertions::assert_impl_all;
86
87    assert_impl_all!(ExecError: Send, Sync);
88}