1#![cfg(feature = "command")]
2
3use crate::IntoResult;
4use std::{
5 fmt::{self, Display},
6 io,
7 process::{Child, ExitStatus, Output},
8};
9
10#[derive(Debug)]
11pub enum Error {
12 SpawnFailed(io::Error),
13 WaitFailed(io::Error),
14 CommandFailed(ExitStatus),
15 CommandFailedWithOutput(Output),
16}
17
18impl Display for Error {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 match self {
21 Self::SpawnFailed(err) => write!(f, "Failed to spawn child process: {}", err),
22 Self::WaitFailed(err) => write!(f, "Failed to wait for child process to exit: {}", err),
23 Self::CommandFailed(status) => {
24 write!(f, "Command didn't complete successfully, ")?;
25 if let Some(exit_code) = status.code() {
26 write!(f, "exiting with code {}.", exit_code)
27 } else {
28 write!(f, "but returned no exit code.")
29 }
30 }
31 Self::CommandFailedWithOutput(Output { status, stderr, .. }) => {
32 write!(f, "{} ", Self::CommandFailed(status.to_owned()))?;
33 if !stderr.is_empty() {
34 write!(f, "stderr contents: {}", String::from_utf8_lossy(stderr))
35 } else {
36 write!(f, "stderr was empty.")
37 }
38 }
39 }
40 }
41}
42
43impl From<io::Error> for Error {
44 fn from(err: io::Error) -> Self {
45 Self::WaitFailed(err)
46 }
47}
48
49impl From<ExitStatus> for Error {
50 fn from(status: ExitStatus) -> Self {
51 Self::CommandFailed(status)
52 }
53}
54
55impl From<Output> for Error {
56 fn from(output: Output) -> Self {
57 Self::CommandFailedWithOutput(output)
58 }
59}
60
61impl Error {
62 pub fn status(&self) -> Option<ExitStatus> {
63 if let Self::CommandFailed(status) = self {
64 Some(status.clone())
65 } else {
66 self.output().map(|output| output.status.clone())
67 }
68 }
69
70 pub fn output(&self) -> Option<&Output> {
71 if let Self::CommandFailedWithOutput(output) = self {
72 Some(output)
73 } else {
74 None
75 }
76 }
77}
78
79pub type Result<T> = std::result::Result<T, Error>;
80
81impl IntoResult<ExitStatus, Error> for ExitStatus {
82 fn into_result(self) -> Result<ExitStatus> {
83 if self.success() {
84 Ok(self)
85 } else {
86 Err(self.into())
87 }
88 }
89}
90
91impl IntoResult<Output, Error> for Output {
92 fn into_result(self) -> Result<Output> {
93 if self.status.success() {
94 Ok(self)
95 } else {
96 Err(self.into())
97 }
98 }
99}
100
101impl IntoResult<ExitStatus, Error> for io::Result<ExitStatus> {
102 fn into_result(self) -> Result<ExitStatus> {
103 self.map_err(Error::WaitFailed)
104 .and_then(ExitStatus::into_result)
105 }
106}
107
108impl IntoResult<Output, Error> for io::Result<Output> {
109 fn into_result(self) -> Result<Output> {
110 self.map_err(Error::WaitFailed)
111 .and_then(Output::into_result)
112 }
113}
114
115impl IntoResult<Child, Error> for io::Result<Child> {
116 fn into_result(self) -> Result<Child> {
117 self.map_err(Error::WaitFailed)
118 }
119}