From 6c5cd5e8960e77f310be95fbccf74603520d074e Mon Sep 17 00:00:00 2001 From: Danil Date: Mon, 1 Jul 2024 14:00:18 +0200 Subject: [PATCH] Add an ability to read exit code and specific streams Signed-off-by: Danil --- src/lib.rs | 33 +++++++++++++++++++++++++++++---- tests/it/main.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9a7ccda..38e4b1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -720,6 +720,16 @@ impl Drop for PushEnv<'_> { } } +/// The output of a finished process for specific stream. Another stream will be printed to the corresponds system stream +#[derive(Debug)] +pub struct StreamOutput { + /// The status (exit code) of the process. + pub exit_status: ExitStatus, + /// The data that the process wrote to stdout or stderr. + /// Any trailing newline or carriage return will be trimmed. + pub stream_output: String +} + /// A builder object for constructing a subprocess. /// /// A [`Cmd`] is usually created with the [`cmd!`] macro. The command exists @@ -968,21 +978,33 @@ impl<'a> Cmd<'a> { /// Run the command and return its stdout as a string. Any trailing newline or carriage return will be trimmed. pub fn read(&self) -> Result { - self.read_stream(false) + Ok(self.read_stream(false)?.stream_output) } - /// Run the command and return its stderr as a string. Any trailing newline or carriage return will be trimmed. + /// Run the command and return its stderr as a string. pub fn read_stderr(&self) -> Result { + Ok(self.read_stream(true)?.stream_output) + } + + /// Run the command and return its stderr as a string and exit status. + /// Any trailing newline or carriage return will be trimmed. + pub fn read_stderr_output(&self) -> Result { self.read_stream(true) } + /// Run the command and return its stdout as a string and exit status. + /// Any trailing newline or carriage return will be trimmed. + pub fn read_stdout_output(&self) -> Result { + self.read_stream(false) + } + /// Run the command and return its output. pub fn output(&self) -> Result { self.output_impl(true, true) } // endregion:running - fn read_stream(&self, read_stderr: bool) -> Result { + fn read_stream(&self, read_stderr: bool) -> Result { let read_stdout = !read_stderr; let output = self.output_impl(read_stdout, read_stderr)?; self.check_status(output.status)?; @@ -997,7 +1019,10 @@ impl<'a> Cmd<'a> { stream.pop(); } - Ok(stream) + Ok(StreamOutput{ + exit_status: output.status, + stream_output: stream, + }) } fn output_impl(&self, read_stdout: bool, read_stderr: bool) -> Result { diff --git a/tests/it/main.rs b/tests/it/main.rs index 3214840..b9be2f8 100644 --- a/tests/it/main.rs +++ b/tests/it/main.rs @@ -54,6 +54,22 @@ fn multiline() { assert_eq!(output, "hello"); } + +#[test] +fn read_with_exit_status() { + let sh = setup(); + let output = cmd!( + sh, + " + xecho hello + " + ) + .read_stdout_output() + .unwrap(); + assert_eq!(output.stream_output, "hello"); + assert!(output.exit_status.success()); +} + #[test] fn interpolation() { let sh = setup(); @@ -185,6 +201,16 @@ fn read_stderr() { assert!(output.contains("snafu")); } +#[test] +fn read_stderr_with_exit_code() { + let sh = setup(); + + let output = cmd!(sh, "xecho -f -e snafu").ignore_status().read_stderr_output().unwrap(); + assert!(output.stream_output.contains("snafu")); + assert!(!output.exit_status.success()) +} + + #[test] fn unknown_command() { let sh = setup();