use std::io;
use std::path::Path;
use std::process::{Command, ExitStatus};
use crate::detection::DetectedPackage;
pub(crate) fn execute_with_cargo_args(
working_dir: &Path,
detected_package: &DetectedPackage,
subcommand: &[String],
) -> Result<ExitStatus, io::Error> {
if subcommand.is_empty() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"No subcommand provided",
));
}
let mut cmd = Command::new("cargo");
cmd.current_dir(working_dir);
let separator_pos = subcommand.iter().position(|arg| arg == "--");
match separator_pos {
Some(pos) => {
if let Some(before_sep) = subcommand.get(..pos) {
cmd.args(before_sep);
}
match detected_package {
DetectedPackage::Package(package_name) => {
cmd.arg("-p").arg(package_name);
}
DetectedPackage::Workspace => {
cmd.arg("--workspace");
}
}
if let Some(after_sep) = subcommand.get(pos..) {
cmd.args(after_sep);
}
}
None => {
cmd.args(subcommand);
match detected_package {
DetectedPackage::Package(package_name) => {
cmd.arg("-p").arg(package_name);
}
DetectedPackage::Workspace => {
cmd.arg("--workspace");
}
}
}
}
cmd.status()
}
#[cfg_attr(test, mutants::skip)]
pub(crate) fn execute_with_env_var(
working_dir: &Path,
env_var: &str,
detected_package: &DetectedPackage,
subcommand: &[String],
) -> Result<ExitStatus, io::Error> {
let Some(first_arg) = subcommand.first() else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"No subcommand provided",
));
};
let mut cmd = Command::new(first_arg);
cmd.current_dir(working_dir);
if let Some(remaining_args) = subcommand.get(1..) {
cmd.args(remaining_args);
}
match detected_package {
DetectedPackage::Package(package_name) => {
cmd.env(env_var, package_name);
}
DetectedPackage::Workspace => {
}
}
cmd.status()
}
#[cfg(all(test, not(miri)))]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use std::fs;
use std::path::Path;
use super::*;
fn create_minimal_workspace() -> tempfile::TempDir {
let temp_dir = tempfile::tempdir().unwrap();
let workspace_root = temp_dir.path();
fs::write(
workspace_root.join("Cargo.toml"),
r#"[workspace]
members = ["test_pkg"]
resolver = "2"
"#,
)
.unwrap();
let test_pkg = workspace_root.join("test_pkg");
fs::create_dir_all(test_pkg.join("src")).unwrap();
fs::write(
test_pkg.join("Cargo.toml"),
r#"[package]
name = "test_pkg"
version = "0.1.0"
edition = "2021"
"#,
)
.unwrap();
fs::write(test_pkg.join("src/lib.rs"), "// minimal lib\n").unwrap();
temp_dir
}
#[test]
fn execute_with_cargo_args_handles_separator() {
let subcommand = ["check".to_string(), "--all".to_string()];
let separator_pos = subcommand.iter().position(|arg| arg == "--");
assert_eq!(separator_pos, None);
let subcommand_with_separator = [
"clippy".to_string(),
"--all-features".to_string(),
"--".to_string(),
"-D".to_string(),
"warnings".to_string(),
];
let separator_pos = subcommand_with_separator.iter().position(|arg| arg == "--");
assert_eq!(separator_pos, Some(2));
let subcommand_edge_case = ["clippy".to_string(), "--".to_string(), "--help".to_string()];
let separator_pos = subcommand_edge_case.iter().position(|arg| arg == "--");
assert_eq!(separator_pos, Some(1));
}
#[test]
fn execute_with_cargo_args_workspace_branch() {
let workspace = create_minimal_workspace();
let result = execute_with_cargo_args(
workspace.path(),
&DetectedPackage::Workspace,
&["tree".to_string(), "--depth".to_string(), "0".to_string()],
);
assert!(result.is_ok());
assert!(result.unwrap().success());
}
#[test]
fn execute_with_cargo_args_workspace_with_separator() {
let workspace = create_minimal_workspace();
let result = execute_with_cargo_args(
workspace.path(),
&DetectedPackage::Workspace,
&[
"clippy".to_string(),
"--".to_string(),
"-A".to_string(),
"warnings".to_string(),
],
);
result.unwrap();
}
#[test]
fn execute_with_cargo_args_no_subcommand_returns_error() {
let result = execute_with_cargo_args(Path::new("."), &DetectedPackage::Workspace, &[]);
let error = result.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
}
#[test]
fn execute_with_env_var_no_subcommand_returns_error() {
let result =
execute_with_env_var(Path::new("."), "TEST_ENV", &DetectedPackage::Workspace, &[]);
let error = result.unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
}
}