use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
pub const EXECUTABLE_EXTENSION: Option<&str> = Some("exe");
pub fn file_name(bare: &str) -> String {
match EXECUTABLE_EXTENSION {
Some(extension) => format!("{bare}.{extension}"),
None => bare.to_owned(),
}
}
pub fn file_name_os(bare: &OsStr) -> OsString {
if Path::new(bare).extension().is_some() {
bare.to_os_string()
} else {
let mut name = bare.to_os_string();
name.push(".exe");
name
}
}
pub fn sibling_of_current_image(bare: &str) -> Option<PathBuf> {
let current = std::env::current_exe().ok()?;
let candidate = current.parent()?.join(file_name(bare));
candidate.is_file().then_some(candidate)
}
pub fn native_library_name(stem: &OsStr) -> OsString {
let mut name = stem.to_os_string();
if Path::new(stem).extension().is_none() {
name.push(".dll");
}
name
}
pub fn find_in_paths(name: &OsStr, directories: &[PathBuf]) -> Option<PathBuf> {
let path = Path::new(name);
let suffixes: Vec<OsString> = if path.extension().is_some() {
vec![OsString::new()]
} else {
std::env::var_os("PATHEXT")
.map(|value| {
value
.to_string_lossy()
.split(';')
.filter(|suffix| !suffix.is_empty())
.map(OsString::from)
.collect()
})
.filter(|suffixes: &Vec<OsString>| !suffixes.is_empty())
.unwrap_or_else(|| [".COM", ".EXE", ".BAT", ".CMD"].map(OsString::from).to_vec())
};
directories.iter().find_map(|directory| {
suffixes.iter().find_map(|suffix| {
let mut candidate_name = name.to_os_string();
candidate_name.push(suffix);
let candidate = directory.join(candidate_name);
candidate.is_file().then_some(candidate)
})
})
}
pub fn stem_matches(path: &OsStr, expected: &str) -> bool {
Path::new(path)
.file_stem()
.and_then(OsStr::to_str)
.is_some_and(|stem| stem.eq_ignore_ascii_case(expected))
}
pub fn unlock_for_replacement(image: &Path) -> std::io::Result<bool> {
let nonce = std::process::id()
^ std::time::UNIX_EPOCH
.elapsed()
.unwrap_or_default()
.subsec_nanos();
let retired = image.with_extension(format!("exe.old.{nonce}"));
std::fs::rename(image, &retired)?;
let _ = std::fs::copy(&retired, image);
Ok(true)
}