use std::path::{Path, PathBuf};
use indexmap::IndexMap;
use vantage_core::{Result, error};
#[derive(Clone, Debug)]
pub struct CmdOutput {
pub stdout: String,
pub stderr: String,
pub exit_code: i32,
}
fn resolve_program(command: &str, base_dir: Option<&Path>) -> PathBuf {
match base_dir {
Some(dir) if command.contains('/') && !Path::new(command).is_absolute() => {
dir.join(command)
}
_ => PathBuf::from(command),
}
}
pub fn run_command(
command: &str,
args: &[String],
env: &IndexMap<String, String>,
pass_path: bool,
base_dir: Option<&Path>,
) -> Result<CmdOutput> {
let mut cmd = std::process::Command::new(resolve_program(command, base_dir));
cmd.args(args);
cmd.env_clear();
if let Some(dir) = base_dir {
cmd.current_dir(dir);
}
if pass_path {
if let Ok(path) = std::env::var("PATH") {
cmd.env("PATH", path);
}
if let Ok(home) = std::env::var("HOME") {
cmd.env("HOME", home);
}
}
for (k, v) in env {
cmd.env(k, v);
}
let output = cmd.output().map_err(|e| {
error!(
"failed to execute command",
command = command.to_string(),
detail = e.to_string()
)
})?;
Ok(CmdOutput {
stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
exit_code: output.status.code().unwrap_or(-1),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bare_name_is_left_for_path_lookup() {
assert_eq!(resolve_program("gh", None), PathBuf::from("gh"));
assert_eq!(
resolve_program("gh", Some(Path::new("/inv"))),
PathBuf::from("gh")
);
}
#[test]
fn relative_path_resolves_against_base_dir() {
assert_eq!(
resolve_program("./scripts/gh-stats.py", Some(Path::new("/inv"))),
PathBuf::from("/inv/./scripts/gh-stats.py")
);
assert_eq!(
resolve_program("scripts/gh-stats.py", Some(Path::new("/inv"))),
PathBuf::from("/inv/scripts/gh-stats.py")
);
}
#[test]
fn relative_path_without_base_dir_is_unchanged() {
assert_eq!(
resolve_program("./scripts/x.py", None),
PathBuf::from("./scripts/x.py")
);
}
#[test]
fn absolute_path_passes_through() {
assert_eq!(
resolve_program("/usr/bin/gh", Some(Path::new("/inv"))),
PathBuf::from("/usr/bin/gh")
);
}
}