diff options
| author | mo8it <mo8it@proton.me> | 2024-07-05 13:39:50 +0200 |
|---|---|---|
| committer | mo8it <mo8it@proton.me> | 2024-07-05 13:39:50 +0200 |
| commit | 7123c7ae3a9605fbe962e4ef0a0f1424cd16fef8 (patch) | |
| tree | c67f7e62bb9a179ae4fdbab492501cb6847e64c7 /src/cmd.rs | |
| parent | 77b687d501771c24bd83294d97b8e6f9ffa92d6b (diff) | |
| parent | 4d9c346a173bb722b929f3ea3c00f84954483e24 (diff) | |
Merge remote-tracking branch 'upstream/main' into fix-enum-variant-inconsistency
Diffstat (limited to 'src/cmd.rs')
| -rw-r--r-- | src/cmd.rs | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/src/cmd.rs b/src/cmd.rs new file mode 100644 index 0000000..6092f53 --- /dev/null +++ b/src/cmd.rs @@ -0,0 +1,93 @@ +use anyhow::{Context, Result}; +use std::{io::Read, path::Path, process::Command}; + +/// Run a command with a description for a possible error and append the merged stdout and stderr. +/// The boolean in the returned `Result` is true if the command's exit status is success. +pub fn run_cmd(mut cmd: Command, description: &str, output: &mut Vec<u8>) -> Result<bool> { + let (mut reader, writer) = os_pipe::pipe() + .with_context(|| format!("Failed to create a pipe to run the command `{description}``"))?; + + let writer_clone = writer.try_clone().with_context(|| { + format!("Failed to clone the pipe writer for the command `{description}`") + })?; + + let mut handle = cmd + .stdout(writer_clone) + .stderr(writer) + .spawn() + .with_context(|| format!("Failed to run the command `{description}`"))?; + + // Prevent pipe deadlock. + drop(cmd); + + reader + .read_to_end(output) + .with_context(|| format!("Failed to read the output of the command `{description}`"))?; + + output.push(b'\n'); + + handle + .wait() + .with_context(|| format!("Failed to wait on the command `{description}` to exit")) + .map(|status| status.success()) +} + +pub struct CargoCmd<'a> { + pub subcommand: &'a str, + pub args: &'a [&'a str], + pub bin_name: &'a str, + pub description: &'a str, + /// RUSTFLAGS="-A warnings" + pub hide_warnings: bool, + /// Added as `--target-dir` if `Self::dev` is true. + pub target_dir: &'a Path, + /// The output buffer to append the merged stdout and stderr. + pub output: &'a mut Vec<u8>, + /// true while developing Rustlings. + pub dev: bool, +} + +impl<'a> CargoCmd<'a> { + /// Run `cargo SUBCOMMAND --bin EXERCISE_NAME … ARGS`. + pub fn run(&mut self) -> Result<bool> { + let mut cmd = Command::new("cargo"); + cmd.arg(self.subcommand); + + // A hack to make `cargo run` work when developing Rustlings. + if self.dev { + cmd.arg("--manifest-path") + .arg("dev/Cargo.toml") + .arg("--target-dir") + .arg(self.target_dir); + } + + cmd.arg("--color") + .arg("always") + .arg("-q") + .arg("--bin") + .arg(self.bin_name) + .args(self.args); + + if self.hide_warnings { + cmd.env("RUSTFLAGS", "-A warnings"); + } + + run_cmd(cmd, self.description, self.output) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_run_cmd() { + let mut cmd = Command::new("echo"); + cmd.arg("Hello"); + + let mut output = Vec::with_capacity(8); + run_cmd(cmd, "echo …", &mut output).unwrap(); + + assert_eq!(output, b"Hello\n\n"); + } +} |
