use std::path::Path;
use std::process::{Command, Stdio};
pub(crate) fn windows_quote_for_cmd(arg: &str) -> String {
let mut escaped = String::with_capacity(arg.len() + 2);
escaped.push('"');
for ch in arg.chars() {
match ch {
'%' => escaped.push_str("%%"),
'"' => escaped.push_str("\"\""),
_ => escaped.push(ch),
}
}
escaped.push('"');
escaped
}
pub(crate) fn resolve_windows_program(program: &str) -> Option<String> {
let program_path = Path::new(program);
let has_separator = program.contains('\\') || program.contains('/');
let has_extension = program_path.extension().is_some();
if has_separator || has_extension {
return Some(program.to_string());
}
let output = Command::new("where")
.arg(program)
.stdout(Stdio::piped())
.stderr(Stdio::null())
.output()
.ok()?;
if !output.status.success() {
return None;
}
String::from_utf8(output.stdout)
.ok()?
.lines()
.map(str::trim)
.filter(|line| !line.is_empty())
.max_by_key(|candidate| windows_program_priority(candidate))
.map(String::from)
}
pub(crate) fn windows_program_priority(candidate: &str) -> u8 {
match Path::new(candidate)
.extension()
.and_then(|ext| ext.to_str())
.map(|ext| ext.to_ascii_lowercase())
{
Some(ext) if ext == "exe" => 5,
Some(ext) if ext == "com" => 4,
Some(ext) if ext == "cmd" => 3,
Some(ext) if ext == "bat" => 2,
Some(_) => 1,
None => 0,
}
}
pub(crate) fn windows_requires_cmd_shell(program: &str) -> bool {
Path::new(program)
.extension()
.and_then(|ext| ext.to_str())
.map(|ext| ext.eq_ignore_ascii_case("bat") || ext.eq_ignore_ascii_case("cmd"))
.unwrap_or(false)
}