#[cfg(unix)]
use std::os::unix::process::CommandExt;
use std::{
ffi::{OsStr, OsString},
path::Path,
process::Command,
};
use snafu::ResultExt;
use crate::{Result, cargo, error};
pub fn run_binary_crate(bin_path: &Path, args: &[OsString]) -> Result<()> {
let mut cmd = Command::new(bin_path);
cmd.args(args);
run(cmd)
}
pub fn run_cargo_subcommand(
bin_path: &Path,
subcommand: &OsStr,
args: &[OsString],
toolchain: Option<&str>,
) -> Result<()> {
let mut cmd = if let Some(toolchain) = toolchain {
let mut cmd = Command::new(cargo::rustup_path(toolchain)?);
cmd.args(["run", toolchain]).arg(bin_path);
cmd
} else {
Command::new(bin_path)
};
cmd.env("CARGO", cargo::cargo_path(toolchain)?);
cmd.arg(subcommand).args(args);
run(cmd)
}
fn run(mut cmd: Command) -> Result<()> {
let bin_path = Path::new(cmd.get_program()).to_path_buf();
#[cfg(unix)]
{
exec_replace(&bin_path, &mut cmd)
}
#[cfg(windows)]
{
spawn_and_wait_windows(&bin_path, &mut cmd)
}
#[cfg(not(any(unix, windows)))]
{
spawn_and_wait_fallback(&bin_path, &mut cmd)
}
}
#[cfg(unix)]
fn exec_replace(bin_path: &Path, cmd: &mut Command) -> Result<()> {
Err(cmd.exec()).with_context(|_| error::ExecFailedSnafu {
path: bin_path.to_owned(),
})
}
#[cfg(windows)]
fn spawn_and_wait_windows(bin_path: &Path, cmd: &mut Command) -> Result<()> {
ctrlc::set_handler(|| {
})
.context(error::ConsoleHandlerFailedSnafu)?;
let mut child = cmd.spawn().with_context(|_| error::SpawnFailedSnafu {
path: bin_path.to_owned(),
})?;
let status = child.wait().context(error::WaitFailedSnafu)?;
let exit_code = status.code().unwrap_or(1);
#[allow(clippy::exit)]
std::process::exit(exit_code)
}
#[cfg(not(any(unix, windows)))]
fn spawn_and_wait_fallback(bin_path: &Path, cmd: &mut Command) -> Result<()> {
let mut child = cmd.spawn().with_context(|_| error::SpawnFailedSnafu {
path: bin_path.to_owned(),
})?;
let status = child.wait().context(error::WaitFailedSnafu)?;
let exit_code = status.code().unwrap_or(1);
#[allow(clippy::exit)]
std::process::exit(exit_code)
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use super::*;
#[test]
fn test_run_binary_crate_nonexistent_binary() {
let result = run_binary_crate(Path::new("/nonexistent/binary"), &[]);
assert_matches!(
result,
Err(error::Error::ExecFailed { .. } | error::Error::SpawnFailed { .. })
);
}
}