command_ext/check/
mod.rs

1//! Extension trait to check the output of a command
2
3use crate::error::CommandExtError;
4use std::process::{Command, Output};
5
6/// Extension trait for [`std::process::Command`] to check the output of a command
7pub trait CommandExtCheck {
8    /// The error type for the result of checking for an error status
9    type Error;
10
11    /// Check the result of a command, returning an error containing the output and
12    /// error stream content if the status is not success
13    fn check(&mut self) -> Result<Output, Self::Error>;
14}
15
16impl CommandExtCheck for Command {
17    type Error = CommandExtError;
18
19    /// Check the result of a command, returning an error containing the status, output
20    /// and error stream content if the status is not success
21    fn check(&mut self) -> Result<Output, Self::Error> {
22        self.output().map_err(CommandExtError::from).and_then(|r| {
23            r.status
24                .success()
25                .then_some(r.clone())
26                .ok_or_else(|| CommandExtError::Check {
27                    status: r.status,
28                    stdout: String::from_utf8_lossy(&r.stdout).to_string(),
29                    stderr: String::from_utf8_lossy(&r.stderr).to_string(),
30                })
31        })
32    }
33}
34
35#[cfg(test)]
36mod test {
37    use std::process::Command;
38
39    use crate::{CommandExtCheck, CommandExtError};
40
41    #[test]
42    #[cfg_attr(miri, ignore)]
43    /// Check that a successful command returns a success output
44    fn test_success() {
45        let output = Command::new("echo").arg("x").check();
46        match output {
47            Ok(output) => assert_eq!(
48                String::from_utf8_lossy(&output.stdout),
49                "x\n",
50                "Output mismatch"
51            ),
52            Err(e) => panic!("Unexpected error from command: {}", e),
53        };
54    }
55
56    #[test]
57    #[cfg_attr(miri, ignore)]
58    /// Test that a command that doesn't exist returns a wrapped IO error
59    fn test_nocmd() {
60        let output = Command::new("asdfasdfasdfasdfjkljkljkl").check();
61
62        match output {
63            Ok(output) => panic!("Unexpected success from command: {:?}", output),
64            Err(e) => assert!(matches!(e, CommandExtError::StdIoError(_))),
65        }
66    }
67
68    #[test]
69    #[cfg_attr(miri, ignore)]
70    /// Test that a command which fails by returning a nonzero status code returns a check error
71    fn test_failure() {
72        let output = Command::new("false").check();
73
74        match output {
75            Ok(output) => panic!("Unexpected success from command: {:?}", output),
76            Err(e) => assert!(matches!(
77                e,
78                CommandExtError::Check {
79                    status: _,
80                    stdout: _,
81                    stderr: _
82                }
83            )),
84        }
85    }
86}