pub trait CommandExt: Sized {
    type Error: From<Error> + Send + Sync;
    type Child;

    // Required methods
    fn output_checked_as<O, R, E>(
        &mut self,
        succeeded: impl Fn(OutputContext<O>) -> Result<R, E>
    ) -> Result<R, E>
       where O: Debug + OutputLike + TryFrom<Output> + Send + Sync + 'static,
             <O as TryFrom<Output>>::Error: Display + Send + Sync,
             E: From<Self::Error> + Send + Sync;
    fn status_checked_as<R, E>(
        &mut self,
        succeeded: impl Fn(OutputContext<ExitStatus>) -> Result<R, E>
    ) -> Result<R, E>
       where E: From<Self::Error>;
    fn spawn_checked(&mut self) -> Result<Self::Child, Self::Error>;
    fn log(&self) -> Result<(), Self::Error>;

    // Provided methods
    fn output_checked_with<O, E>(
        &mut self,
        succeeded: impl Fn(&O) -> Result<(), Option<E>>
    ) -> Result<O, Self::Error>
       where O: Debug + OutputLike + TryFrom<Output> + Send + Sync + 'static,
             <O as TryFrom<Output>>::Error: Display + Send + Sync,
             E: Debug + Display + Send + Sync + 'static { ... }
    fn output_checked(&mut self) -> Result<Output, Self::Error> { ... }
    fn output_checked_utf8(&mut self) -> Result<Utf8Output, Self::Error> { ... }
    fn output_checked_with_utf8<E>(
        &mut self,
        succeeded: impl Fn(&Utf8Output) -> Result<(), Option<E>>
    ) -> Result<Utf8Output, Self::Error>
       where E: Display + Debug + Send + Sync + 'static { ... }
    fn status_checked_with<E>(
        &mut self,
        succeeded: impl Fn(ExitStatus) -> Result<(), Option<E>>
    ) -> Result<ExitStatus, Self::Error>
       where E: Debug + Display + Send + Sync + 'static { ... }
    fn status_checked(&mut self) -> Result<ExitStatus, Self::Error> { ... }
}
Expand description

Extension trait for Command.

CommandExt methods check the exit status of the command (or perform user-supplied validation logic) and produced detailed, helpful error messages when they fail:

use std::process::Command;
use command_error::CommandExt;

let err = Command::new("sh")
    .args(["-c", "echo puppy; false"])
    .output_checked_utf8()
    .unwrap_err();

assert_eq!(
    err.to_string(),
    indoc!(
        "`sh` failed: exit status: 1
        Command failed: `sh -c 'echo puppy; false'`
        Stdout:
          puppy"
    )
);

With the tracing feature enabled, commands will be logged before they run.

§Method overview

MethodOutput decodingErrors
output_checkedBytesIf non-zero exit code
output_checked_withArbitraryCustom
output_checked_asArbitraryCustom, with arbitrary error type
output_checked_utf8UTF-8If non-zero exit code
output_checked_with_utf8UTF-8Custom
status_checkedNoneIf non-zero exit code
status_checked_withNoneCustom
status_checked_asNoneCustom, with arbitrary error type

Required Associated Types§

source

type Error: From<Error> + Send + Sync

The error type returned from methods on this trait.

source

type Child

The type of child process produced.

Required Methods§

source

fn output_checked_as<O, R, E>( &mut self, succeeded: impl Fn(OutputContext<O>) -> Result<R, E> ) -> Result<R, E>
where O: Debug + OutputLike + TryFrom<Output> + Send + Sync + 'static, <O as TryFrom<Output>>::Error: Display + Send + Sync, E: From<Self::Error> + Send + Sync,

Run a command, capturing its output. succeeded is called and returned to determine if the command succeeded.

See Command::output for more information.

This is the most general CommandExt method, and gives the caller full control over success logic and the output and errors produced.

let err = Command::new("cat")
    .arg("tests/data/incomplete.json")
    .output_checked_as(|context: OutputContext<Output>| {
        serde_json::from_slice(&context.output().stdout)
            .map_err(|err| context.error_msg(err))
    })
    .unwrap_err();

assert_eq!(
    err.to_string(),
    indoc!(
        r#"`cat` failed: EOF while parsing a list at line 4 column 11
        exit status: 0
        Command failed: `cat tests/data/incomplete.json`
        Stdout:
          [
              "cuppy",
              "dog",
              "city","#
    )
);

Note that the closure takes the output as raw bytes but the error message contains the output decoded as UTF-8. In this example, the decoding only happens in the error case, but if you request an OutputContext<Utf8Output>, the decoded data will be reused for the error message.

The OutputContext passed to the closure contains information about the command’s Output (including its ExitStatus), the command that ran (the program name and its arguments), and methods for constructing detailed error messages (with or without additional context information).

source

fn status_checked_as<R, E>( &mut self, succeeded: impl Fn(OutputContext<ExitStatus>) -> Result<R, E> ) -> Result<R, E>
where E: From<Self::Error>,

Run a command without capturing its output. succeeded is called and returned to determine if the command succeeded.

This gives the caller full control over success logic and the output and errors produced.

let succeeded = |context: OutputContext<ExitStatus>| {
    match context.status().code() {
        Some(code) => Ok(code),
        None => Err(context.error_msg("no exit code")),
    }
};

let code = Command::new("true")
    .status_checked_as(succeeded)
    .unwrap();
assert_eq!(code, 0);

let err = Command::new("sh")
    .args(["-c", "kill \"$$\""])
    .status_checked_as(succeeded)
    .unwrap_err();
assert_eq!(
    err.to_string(),
    indoc!(
        r#"`sh` failed: no exit code
        signal: 15 (SIGTERM)
        Command failed: `sh -c 'kill "$$"'`"#
    )
);

To error on non-zero exit codes, use CommandExt::status_checked.

See Command::status for more information.

source

fn spawn_checked(&mut self) -> Result<Self::Child, Self::Error>

Spawn a command.

The returned child contains context information about the command that produced it, which can be used to produce detailed error messages if the child process fails.

See Command::spawn for more information.

let err = Command::new("ooga booga")
    .spawn_checked()
    .unwrap_err();

assert_eq!(
    err.to_string(),
    "Failed to execute `'ooga booga'`: No such file or directory (os error 2)"
);
source

fn log(&self) -> Result<(), Self::Error>

Log the command that will be run.

With the tracing feature enabled, this will emit a debug-level log with message Executing command and a command field containing the command and arguments shell-quoted.

Provided Methods§

source

fn output_checked_with<O, E>( &mut self, succeeded: impl Fn(&O) -> Result<(), Option<E>> ) -> Result<O, Self::Error>
where O: Debug + OutputLike + TryFrom<Output> + Send + Sync + 'static, <O as TryFrom<Output>>::Error: Display + Send + Sync, E: Debug + Display + Send + Sync + 'static,

Run a command, capturing its output. succeeded is called and used to determine if the command succeeded and (optionally) to add an additional message to the error returned.

This method is best if you want to consider a command successful if it has a non-zero exit code, or if its output contains some special string. If you’d like to additionally produce output that can’t be produced with TryFrom<Output> (such as to deserialize a data structure), CommandExt::output_checked_as provides full control over the produced result.

See Command::output for more information.

let output = Command::new("sh")
    .args(["-c", "echo puppy && exit 2"])
    .output_checked_with(|output: &Output| {
        if let Some(2) = output.status.code() {
            Ok(())
        } else {
            // Don't add any additional context to the error message:
            Err(None::<String>)
        }
    })
    .unwrap();

assert_eq!(
    output.status.code(),
    Some(2),
);

Note that due to the generic error parameter, you’ll need to annotate None return values with a Displayable type — try String or any std::error::Error type in scope.

Command::output_checked_with can also be used to convert the output to any type that implements TryFrom<Output> before running succeeded:

let err = Command::new("sh")
    .args(["-c", "echo kitty && kill -9 \"$$\""])
    .output_checked_with(|output: &Utf8Output| {
        if output.status.success() && output.stdout.trim() == "puppy" {
            Ok(())
        } else {
            Err(Some("didn't find any puppy!"))
        }
    })
    .unwrap_err();

assert_eq!(
    err.to_string(),
    indoc!(
        r#"`sh` failed: didn't find any puppy!
        signal: 9 (SIGKILL)
        Command failed: `sh -c 'echo kitty && kill -9 "$$"'`
        Stdout:
          kitty"#
    )
);
source

fn output_checked(&mut self) -> Result<Output, Self::Error>

Run a command, capturing its output. If the command exits with a non-zero exit code, an error is raised.

Error messages are detailed and contain information about the command that was run and its output:

let err = Command::new("ooby-gooby")
    .output_checked()
    .unwrap_err();

assert_eq!(
    err.to_string(),
    "Failed to execute `ooby-gooby`: No such file or directory (os error 2)"
);

let err = Command::new("sh")
    .args(["-c", "echo puppy && exit 1"])
    .output_checked()
    .unwrap_err();
assert_eq!(
    err.to_string(),
    indoc!(
        "`sh` failed: exit status: 1
        Command failed: `sh -c 'echo puppy && exit 1'`
        Stdout:
          puppy"
    )
);

If the command fails, output will be decoded as UTF-8 for display in error messages, but otherwise no output decoding is performed. To decode output as UTF-8, use CommandExt::output_checked_utf8. To decode as other formats, use CommandExt::output_checked_with.

See Command::output for more information.

source

fn output_checked_utf8(&mut self) -> Result<Utf8Output, Self::Error>

Run a command, capturing its output and decoding it as UTF-8. If the command exits with a non-zero exit code or if its output contains invalid UTF-8, an error is raised.

See CommandExt::output_checked and Command::output for more information.

let output = Command::new("echo")
    .arg("puppy")
    .output_checked_utf8()
    .unwrap();

assert_eq!(
    output,
    Utf8Output {
        status: ExitStatus::default(),
        stdout: "puppy\n".into(),
        stderr: "".into(),
    },
);
source

fn output_checked_with_utf8<E>( &mut self, succeeded: impl Fn(&Utf8Output) -> Result<(), Option<E>> ) -> Result<Utf8Output, Self::Error>
where E: Display + Debug + Send + Sync + 'static,

Run a command, capturing its output and decoding it as UTF-8. succeeded is called and used to determine if the command succeeded and (optionally) to add an additional message to the error returned.

See CommandExt::output_checked_with and Command::output for more information.

let output = Command::new("sh")
    .args(["-c", "echo puppy; exit 1"])
    .output_checked_with_utf8(|output| {
        if output.stdout.contains("puppy") {
            Ok(())
        } else {
            Err(None::<String>)
        }
    })
    .unwrap();

assert_eq!(output.stdout, "puppy\n");
assert_eq!(output.status.code(), Some(1));
source

fn status_checked_with<E>( &mut self, succeeded: impl Fn(ExitStatus) -> Result<(), Option<E>> ) -> Result<ExitStatus, Self::Error>
where E: Debug + Display + Send + Sync + 'static,

Run a command without capturing its output. succeeded is called and used to determine if the command succeeded and (optionally) to add an additional message to the error returned.

let status = Command::new("false")
    .status_checked_with(|status| {
        match status.code() {
            // Exit codes 0 and 1 are OK.
            Some(0) | Some(1) => Ok(()),
            // Other exit codes are errors.
            _ => Err(None::<String>)
        }
    })
    .unwrap();
assert_eq!(status.code(), Some(1));

See Command::status for more information.

source

fn status_checked(&mut self) -> Result<ExitStatus, Self::Error>

Run a command without capturing its output. If the command exits with a non-zero status code, an error is raised containing information about the command that was run:

let err = Command::new("sh")
    .args(["-c", "exit 1"])
    .status_checked()
    .unwrap_err();

assert_eq!(
    err.to_string(),
    indoc!(
        "`sh` failed: exit status: 1
        Command failed: `sh -c 'exit 1'`"
    )
);

See Command::status for more information.

Object Safety§

This trait is not object safe.

Implementations on Foreign Types§

source§

impl CommandExt for Command

§

type Error = Error

§

type Child = ChildContext<Child>

source§

fn log(&self) -> Result<(), Self::Error>

source§

fn output_checked_as<O, R, E>( &mut self, succeeded: impl Fn(OutputContext<O>) -> Result<R, E> ) -> Result<R, E>
where O: Debug + OutputLike + TryFrom<Output> + Send + Sync + 'static, <O as TryFrom<Output>>::Error: Display + Send + Sync, E: From<Self::Error> + Send + Sync,

source§

fn status_checked_as<R, E>( &mut self, succeeded: impl Fn(OutputContext<ExitStatus>) -> Result<R, E> ) -> Result<R, E>
where E: From<Self::Error>,

source§

fn spawn_checked(&mut self) -> Result<Self::Child, Self::Error>

Implementors§