use std::collections::HashMap;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;
pub struct CommandFinder {
cache: HashMap<OsString, Option<PathBuf>>,
path: OsString,
}
impl CommandFinder {
pub fn new() -> Self {
Self {
cache: HashMap::new(),
path: env::var_os("PATH").unwrap_or_default(),
}
}
pub fn maybe_have<S: AsRef<OsStr>>(&mut self, cmd: S) -> Option<PathBuf> {
let cmd: OsString = cmd.as_ref().into();
let path = self.path.clone();
self.cache
.entry(cmd.clone())
.or_insert_with(|| {
for path in env::split_paths(&path) {
let target = path.join(&cmd);
let mut cmd_alt = cmd.clone();
cmd_alt.push(".exe");
if target.is_file() || target.with_extension("exe").exists() || target.join(&cmd_alt).exists()
{
return Some(target);
}
}
None
})
.clone()
}
pub fn must_have<S: AsRef<OsStr>>(&mut self, cmd: S) -> PathBuf {
self.maybe_have(&cmd).unwrap_or_else(|| {
panic!("\n\ncouldn't find required command: {:?}\n\n", cmd.as_ref());
})
}
}