1use std::{
2 io,
3 process::{ExitStatus, Output},
4};
5
6#[cfg(unix)]
7use std::os::unix::process::ExitStatusExt as _;
8
9pub trait AsResult<T, E> {
11 fn as_result(&self) -> Result<T, E>;
13}
14
15pub trait IntoResult<T, E> {
17 fn into_result(self) -> Result<T, E>;
19}
20
21pub trait MapResult<T, E> {
23 fn map_result(self) -> Result<T, E>;
25}
26
27impl AsResult<(), io::Error> for ExitStatus {
28 fn as_result(&self) -> io::Result<()> {
29 Err(if self.success() {
30 return Ok(());
31 } else if let Some(127) = self.code() {
32 io::Error::new(io::ErrorKind::NotFound, "command was not found")
33 } else {
34 #[cfg(unix)]
35 {
36 if let Some(signal) = self.signal() {
37 return Err(io::Error::new(
38 io::ErrorKind::Other,
39 format!("terminated with signal {}", signal),
40 ));
41 }
42 }
43
44 io::Error::new(io::ErrorKind::Other, format!("status is unknown: {}", self))
45 })
46 }
47}
48
49impl IntoResult<(), io::Error> for ExitStatus {
50 fn into_result(self) -> io::Result<()> {
51 self.as_result()
52 }
53}
54
55impl IntoResult<Output, io::Error> for Output {
56 fn into_result(self) -> io::Result<Output> {
57 self.status.as_result().map(|_| self)
58 }
59}
60
61impl MapResult<(), io::Error> for io::Result<ExitStatus> {
62 fn map_result(self) -> io::Result<()> {
63 self.and_then(IntoResult::into_result)
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use std::process::Command;
71
72 #[test]
73 fn command() {
74 Command::new("/bin/echo")
75 .arg("hello world")
76 .status()
77 .and_then(IntoResult::into_result)
78 .unwrap();
79
80 Command::new("/bin/echo")
81 .arg("hello world")
82 .status()
83 .unwrap()
84 .into_result()
85 .unwrap();
86
87 Command::new("/bin/echo")
88 .arg("hello world")
89 .status()
90 .map_result()
91 .unwrap()
92 }
93}