use std::{
ffi::{OsStr, OsString},
path::{Path, PathBuf},
};
use bstr::{BString, ByteSlice};
use std::sync::LazyLock;
use crate::env::git::EXE_NAME;
mod auxiliary;
mod git;
pub fn installation_config() -> Option<&'static Path> {
git::install_config_path().and_then(|p| crate::try_from_byte_slice(p).ok())
}
pub fn installation_config_prefix() -> Option<&'static Path> {
installation_config().map(git::config_to_base_path)
}
pub fn shell() -> &'static OsStr {
static PATH: LazyLock<OsString> = LazyLock::new(|| {
if cfg!(windows) {
auxiliary::find_git_associated_windows_executable_with_fallback("sh")
} else {
"/bin/sh".into()
}
});
PATH.as_ref()
}
pub fn exe_invocation() -> &'static Path {
if cfg!(windows) {
static EXECUTABLE_PATH: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
std::env::split_paths(&std::env::var_os("PATH")?)
.chain(git::ALTERNATIVE_LOCATIONS.iter().map(Into::into))
.find_map(|prefix| {
let full_path = prefix.join(EXE_NAME);
full_path.is_file().then_some(full_path)
})
.map(|exe_path| {
let is_in_alternate_location = git::ALTERNATIVE_LOCATIONS
.iter()
.any(|prefix| exe_path.strip_prefix(prefix).is_ok());
if is_in_alternate_location {
exe_path
} else {
EXE_NAME.into()
}
})
});
EXECUTABLE_PATH.as_deref().unwrap_or(Path::new(git::EXE_NAME))
} else {
Path::new("git")
}
}
pub fn xdg_config(file: &str, env_var: &mut dyn FnMut(&str) -> Option<OsString>) -> Option<PathBuf> {
env_var("XDG_CONFIG_HOME")
.map(|home| {
let mut p = PathBuf::from(home);
p.push("git");
p.push(file);
p
})
.or_else(|| {
env_var("HOME").map(|home| {
let mut p = PathBuf::from(home);
p.push(".config");
p.push("git");
p.push(file);
p
})
})
}
static GIT_CORE_DIR: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
let mut cmd = std::process::Command::new(exe_invocation());
#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x08000000;
cmd.creation_flags(CREATE_NO_WINDOW);
}
let output = cmd.arg("--exec-path").output().ok()?;
if !output.status.success() {
return None;
}
BString::new(output.stdout)
.strip_suffix(b"\n")?
.to_path()
.ok()?
.to_owned()
.into()
});
pub fn core_dir() -> Option<&'static Path> {
GIT_CORE_DIR.as_deref()
}
fn system_prefix_from_core_dir<F>(core_dir_func: F) -> Option<PathBuf>
where
F: Fn() -> Option<&'static Path>,
{
let path = core_dir_func()?;
let one_past_prefix = path.components().enumerate().find_map(|(idx, c)| {
matches!(c,std::path::Component::Normal(name) if name.to_str() == Some("libexec")).then_some(idx)
})?;
Some(path.components().take(one_past_prefix.checked_sub(1)?).collect())
}
fn system_prefix_from_exepath_var<F>(var_os_func: F) -> Option<PathBuf>
where
F: Fn(&str) -> Option<OsString>,
{
let root = var_os_func("EXEPATH").map(PathBuf::from).filter(|r| r.is_absolute())?;
let mut candidates = ["clangarm64", "mingw64", "mingw32"]
.iter()
.map(|component| root.join(component))
.filter(|candidate| candidate.is_dir());
let path = candidates.next()?;
match candidates.next() {
Some(_) => None, None => Some(path),
}
}
pub fn system_prefix() -> Option<&'static Path> {
if cfg!(windows) {
static PREFIX: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
system_prefix_from_exepath_var(|key| std::env::var_os(key))
.or_else(|| system_prefix_from_core_dir(core_dir))
});
PREFIX.as_deref()
} else {
Path::new("/").into()
}
}
#[cfg(target_family = "wasm")]
pub fn home_dir() -> Option<PathBuf> {
std::env::var("HOME").map(PathBuf::from).ok()
}
#[cfg(not(target_family = "wasm"))]
pub fn home_dir() -> Option<PathBuf> {
std::env::var_os("HOME").map(Into::into).or_else(std::env::home_dir)
}
pub fn var(name: &str) -> Option<OsString> {
if name == "HOME" {
home_dir().map(PathBuf::into_os_string)
} else {
std::env::var_os(name)
}
}
#[cfg(test)]
mod tests;