use std::env;
use std::ffi::OsStr;
use std::path::PathBuf;
use std::process::Command;
#[cfg(windows)]
pub fn find_command_on_path<T: AsRef<OsStr>>(name: T) -> Option<PathBuf> {
let Ok(system_path) = env::var("PATH") else {
return None;
};
let Ok(path_ext) = env::var("PATHEXT") else {
return None;
};
let exts = path_ext.split(';').collect::<Vec<_>>();
let name = name.as_ref();
for path_dir in env::split_paths(&system_path) {
for ext in &exts {
let mut file_name = name.to_os_string();
file_name.push(ext);
let path = path_dir.join(file_name);
if path.exists() {
return Some(path);
}
}
}
None
}
#[cfg(not(windows))]
pub fn find_command_on_path<T: AsRef<OsStr>>(name: T) -> Option<PathBuf> {
let Ok(system_path) = env::var("PATH") else {
return None;
};
let name = name.as_ref();
for path_dir in env::split_paths(&system_path) {
let path = path_dir.join(name);
if path.exists() {
return Some(path);
}
}
None
}
pub fn is_command_on_path<T: AsRef<OsStr>>(name: T) -> bool {
find_command_on_path(name.as_ref()).is_some()
}
pub fn create_process_command<T: AsRef<OsStr>, I: IntoIterator<Item = A>, A: AsRef<OsStr>>(
bin: T,
args: I,
) -> Command {
let bin = bin.as_ref();
let bin_path = if bin
.as_encoded_bytes()
.iter()
.any(|b| b.eq_ignore_ascii_case(&b'/') || b.eq_ignore_ascii_case(&b'\\'))
{
PathBuf::from(bin)
} else {
find_command_on_path(bin).unwrap_or_else(|| bin.into())
};
match bin_path.extension().map(|e| e.to_str().unwrap()) {
Some("ps1" | "cmd" | "bat") => {
let args = args
.into_iter()
.map(|a| String::from_utf8_lossy(a.as_ref().as_encoded_bytes()).to_string())
.collect::<Vec<_>>();
let mut cmd =
Command::new(find_command_on_path("pwsh").unwrap_or_else(|| "powershell".into()));
cmd.arg("-Command");
cmd.arg(format!("{} {}", bin_path.display(), shell_words::join(args)).trim());
cmd
}
_ => {
let mut cmd = Command::new(bin_path);
cmd.args(args);
cmd
}
}
}