Skip to main content

into_result/
command.rs

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}