command_error/
error.rs

1use std::fmt::Display;
2
3#[cfg(doc)]
4use std::process::Child;
5#[cfg(doc)]
6use std::process::Command;
7#[cfg(doc)]
8use std::process::Output;
9
10use crate::output_conversion_error::OutputConversionError;
11use crate::ExecError;
12use crate::OutputError;
13use crate::WaitError;
14
15#[cfg(doc)]
16use crate::CommandExt;
17#[cfg(feature = "miette")]
18use miette::Diagnostic;
19
20/// An error produced by a [`Command`] failure.
21#[derive(Debug)]
22#[non_exhaustive]
23pub enum Error {
24    /// An execution failure, when a [`Command`] fails to start or [`Child::wait`].
25    Exec(ExecError),
26    /// A failure to wait for a [`Command`].
27    ///
28    /// See: [`Child::wait`].
29    Wait(WaitError),
30    /// An output failure, when a [`Command`] fails by returning a non-zero exit code (or in other
31    /// cases, when custom validation logic is supplied in methods like
32    /// [`CommandExt::output_checked_with`]).
33    ///
34    /// Note that this is also raised when non-capturing methods like
35    /// [`CommandExt::status_checked`] fail.
36    Output(OutputError),
37    /// An output conversion error, when [`Output`] fails to convert to a custom format as
38    /// requested by methods like [`CommandExt::output_checked_utf8`].
39    Conversion(OutputConversionError),
40}
41
42impl Error {
43    #[cfg(feature = "miette")]
44    fn as_inner_diagnostic(&self) -> &(dyn Diagnostic + Send + Sync + 'static) {
45        match self {
46            Error::Exec(inner) => inner,
47            Error::Wait(inner) => inner,
48            Error::Output(inner) => inner,
49            Error::Conversion(inner) => inner,
50        }
51    }
52}
53
54impl Display for Error {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        match self {
57            Error::Exec(error) => write!(f, "{}", error),
58            Error::Wait(error) => write!(f, "{}", error),
59            Error::Output(error) => write!(f, "{}", error),
60            Error::Conversion(error) => write!(f, "{}", error),
61        }
62    }
63}
64
65impl From<ExecError> for Error {
66    fn from(error: ExecError) -> Self {
67        Self::Exec(error)
68    }
69}
70
71impl From<WaitError> for Error {
72    fn from(error: WaitError) -> Self {
73        Self::Wait(error)
74    }
75}
76
77impl From<OutputError> for Error {
78    fn from(error: OutputError) -> Self {
79        Self::Output(error)
80    }
81}
82
83impl From<OutputConversionError> for Error {
84    fn from(error: OutputConversionError) -> Self {
85        Self::Conversion(error)
86    }
87}
88
89impl std::error::Error for Error {}
90
91#[cfg(feature = "miette")]
92impl Diagnostic for Error {
93    fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
94        self.as_inner_diagnostic().code()
95    }
96
97    fn severity(&self) -> Option<miette::Severity> {
98        self.as_inner_diagnostic().severity()
99    }
100
101    fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
102        self.as_inner_diagnostic().help()
103    }
104
105    fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
106        self.as_inner_diagnostic().url()
107    }
108
109    fn source_code(&self) -> Option<&dyn miette::SourceCode> {
110        self.as_inner_diagnostic().source_code()
111    }
112
113    fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
114        self.as_inner_diagnostic().labels()
115    }
116
117    fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
118        self.as_inner_diagnostic().related()
119    }
120
121    fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
122        self.as_inner_diagnostic().diagnostic_source()
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use static_assertions::assert_impl_all;
130
131    assert_impl_all!(Error: Send, Sync);
132}