2024-04-26 22:14:59 -04:00
|
|
|
use anyhow::{Context, Result};
|
2024-07-28 14:30:23 -04:00
|
|
|
use std::{
|
|
|
|
io::Read,
|
|
|
|
path::Path,
|
|
|
|
process::{Command, Stdio},
|
|
|
|
};
|
2024-04-26 22:14:59 -04:00
|
|
|
|
2024-05-13 15:40:40 -04:00
|
|
|
/// 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.
|
2024-07-28 14:30:23 -04:00
|
|
|
pub fn run_cmd(mut cmd: Command, description: &str, output: Option<&mut Vec<u8>>) -> Result<bool> {
|
|
|
|
let spawn = |mut cmd: Command| {
|
|
|
|
// NOTE: The closure drops `cmd` which prevents a pipe deadlock.
|
2024-07-31 19:07:31 -04:00
|
|
|
cmd.stdin(Stdio::null())
|
|
|
|
.spawn()
|
2024-07-28 14:30:23 -04:00
|
|
|
.with_context(|| format!("Failed to run the command `{description}`"))
|
|
|
|
};
|
2024-04-26 22:14:59 -04:00
|
|
|
|
2024-07-28 14:30:23 -04:00
|
|
|
let mut handle = if let Some(output) = output {
|
|
|
|
let (mut reader, writer) = os_pipe::pipe().with_context(|| {
|
|
|
|
format!("Failed to create a pipe to run the command `{description}``")
|
|
|
|
})?;
|
2024-04-26 22:14:59 -04:00
|
|
|
|
2024-07-28 14:30:23 -04:00
|
|
|
let writer_clone = writer.try_clone().with_context(|| {
|
|
|
|
format!("Failed to clone the pipe writer for the command `{description}`")
|
|
|
|
})?;
|
2024-04-26 22:14:59 -04:00
|
|
|
|
2024-07-28 14:30:23 -04:00
|
|
|
cmd.stdout(writer_clone).stderr(writer);
|
|
|
|
let handle = spawn(cmd)?;
|
2024-04-26 22:14:59 -04:00
|
|
|
|
2024-07-28 14:30:23 -04:00
|
|
|
reader
|
|
|
|
.read_to_end(output)
|
|
|
|
.with_context(|| format!("Failed to read the output of the command `{description}`"))?;
|
2024-04-26 22:14:59 -04:00
|
|
|
|
2024-07-28 14:30:23 -04:00
|
|
|
output.push(b'\n');
|
|
|
|
|
|
|
|
handle
|
|
|
|
} else {
|
|
|
|
cmd.stdout(Stdio::null()).stderr(Stdio::null());
|
|
|
|
spawn(cmd)?
|
|
|
|
};
|
2024-04-26 22:14:59 -04:00
|
|
|
|
|
|
|
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],
|
2024-06-01 15:48:15 -04:00
|
|
|
pub bin_name: &'a str,
|
2024-04-26 22:14:59 -04:00
|
|
|
pub description: &'a str,
|
2024-05-13 15:40:40 -04:00
|
|
|
/// RUSTFLAGS="-A warnings"
|
2024-04-26 22:14:59 -04:00
|
|
|
pub hide_warnings: bool,
|
2024-05-13 15:40:40 -04:00
|
|
|
/// Added as `--target-dir` if `Self::dev` is true.
|
2024-04-26 22:14:59 -04:00
|
|
|
pub target_dir: &'a Path,
|
2024-05-13 15:40:40 -04:00
|
|
|
/// The output buffer to append the merged stdout and stderr.
|
2024-07-28 14:30:23 -04:00
|
|
|
pub output: Option<&'a mut Vec<u8>>,
|
2024-05-13 15:40:40 -04:00
|
|
|
/// true while developing Rustlings.
|
2024-04-26 22:14:59 -04:00
|
|
|
pub dev: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> CargoCmd<'a> {
|
2024-05-13 15:40:40 -04:00
|
|
|
/// Run `cargo SUBCOMMAND --bin EXERCISE_NAME … ARGS`.
|
2024-07-28 14:30:23 -04:00
|
|
|
pub fn run(self) -> Result<bool> {
|
2024-04-26 22:14:59 -04:00
|
|
|
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")
|
2024-06-01 15:48:15 -04:00
|
|
|
.arg(self.bin_name)
|
2024-04-26 22:14:59 -04:00
|
|
|
.args(self.args);
|
|
|
|
|
|
|
|
if self.hide_warnings {
|
|
|
|
cmd.env("RUSTFLAGS", "-A warnings");
|
|
|
|
}
|
|
|
|
|
|
|
|
run_cmd(cmd, self.description, self.output)
|
|
|
|
}
|
|
|
|
}
|
2024-05-01 12:08:18 -04:00
|
|
|
|
|
|
|
#[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);
|
2024-07-28 14:30:23 -04:00
|
|
|
run_cmd(cmd, "echo …", Some(&mut output)).unwrap();
|
2024-05-01 12:08:18 -04:00
|
|
|
|
|
|
|
assert_eq!(output, b"Hello\n\n");
|
|
|
|
}
|
|
|
|
}
|