use std::{
ffi::OsString,
path::{Path, PathBuf},
};
use std::sync::LazyLock;
const MSYS_USR_VARIANTS: &[&str] = &["mingw64", "mingw32", "clangarm64", "clang64", "clang32", "ucrt64"];
fn git_for_windows_root() -> Option<&'static Path> {
static GIT_ROOT: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
super::core_dir()
.filter(|core| {
core.is_absolute() && core.ends_with("libexec/git-core")
})
.and_then(|core| core.ancestors().nth(2))
.filter(|prefix| {
MSYS_USR_VARIANTS.iter().any(|name| prefix.ends_with(name))
})
.and_then(|prefix| prefix.parent())
.map(Into::into)
});
GIT_ROOT.as_deref()
}
const BIN_DIR_FRAGMENTS: &[&str] = &["bin", "usr/bin"];
fn find_git_associated_windows_executable(stem: &str) -> Option<OsString> {
let git_root = git_for_windows_root()?;
BIN_DIR_FRAGMENTS
.iter()
.map(|bin_dir_fragment| {
let mut raw_path = OsString::from(git_root);
raw_path.push("/");
raw_path.push(bin_dir_fragment);
raw_path.push("/");
raw_path.push(stem);
raw_path.push(".exe");
raw_path
})
.find(|raw_path| Path::new(raw_path).is_file())
}
pub(super) fn find_git_associated_windows_executable_with_fallback(stem: &str) -> OsString {
find_git_associated_windows_executable(stem).unwrap_or_else(|| {
let mut raw_path = OsString::from(stem);
raw_path.push(".exe");
raw_path
})
}
#[cfg(test)]
mod tests {
use std::path::Path;
const SHOULD_FIND: &[&str] = &[
"sh", "bash", "dash", "diff", "tar", "less", "sed", "awk", "perl", "cygpath",
];
const SHOULD_NOT_FIND: &[&str] = &[
"nonexistent-command",
"cmd",
"powershell",
"explorer",
"git-credential-manager",
"git-daemon",
];
#[test]
#[cfg_attr(not(windows), ignore = "only meaningful on Windows")]
fn find_git_associated_windows_executable() {
for stem in SHOULD_FIND {
let path = super::find_git_associated_windows_executable(stem);
assert!(path.is_some(), "should find {stem:?}");
}
}
#[test]
#[cfg_attr(not(windows), ignore = "only meaningful on Windows")]
fn find_git_associated_windows_executable_no_extra() {
for stem in SHOULD_NOT_FIND {
let path = super::find_git_associated_windows_executable(stem);
assert_eq!(path, None, "should not find {stem:?}");
}
}
#[test]
#[cfg_attr(not(windows), ignore = "only meaningful on Windows")]
fn find_git_associated_windows_executable_with_fallback() {
for stem in SHOULD_FIND {
let path = super::find_git_associated_windows_executable_with_fallback(stem);
assert!(Path::new(&path).is_absolute(), "should find {stem:?}");
}
}
#[test]
#[cfg_attr(not(windows), ignore = "only meaningful on Windows")]
fn find_git_associated_windows_executable_with_fallback_falls_back() {
for stem in SHOULD_NOT_FIND {
let path = super::find_git_associated_windows_executable_with_fallback(stem)
.to_str()
.expect("valid Unicode")
.to_owned();
assert_eq!(path, format!("{stem}.exe"), "should fall back for {stem:?}");
}
}
}