use std::{ ffi::OsString, path::{Path, PathBuf}, }; use bstr::{BString, ByteSlice}; mod git; /// Return the location at which installation specific git configuration file can be found, or `None` /// if the binary could not be executed or its results could not be parsed. /// /// ### Performance /// /// This invokes the git binary which is slow on windows. pub fn installation_config() -> Option<&'static Path> { git::install_config_path().and_then(|p| crate::try_from_byte_slice(p).ok()) } /// Return the location at which git installation specific configuration files are located, or `None` if the binary /// could not be executed or its results could not be parsed. /// /// ### Performance /// /// This invokes the git binary which is slow on windows. pub fn installation_config_prefix() -> Option<&'static Path> { installation_config().map(git::config_to_base_path) } /// Returns the fully qualified path in the *xdg-home* directory (or equivalent in the home dir) to `file`, /// accessing `env_var()` to learn where these bases are. /// /// Note that the `HOME` directory should ultimately come from [`home_dir()`] as it handles windows correctly. /// The same can be achieved by using [`var()`] as `env_var`. pub fn xdg_config(file: &str, env_var: &mut dyn FnMut(&str) -> Option) -> Option { 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 }) }) } /// Returns the platform dependent system prefix or `None` if it cannot be found (right now only on windows). /// /// ### Performance /// /// On windows, the slowest part is the launch of the `git.exe` executable in the PATH, which only happens when launched /// from outside of the `msys2` shell. /// /// ### When `None` is returned /// /// This happens only windows if the git binary can't be found at all for obtaining its executable path, or if the git binary /// wasn't built with a well-known directory structure or environment. pub fn system_prefix() -> Option<&'static Path> { if cfg!(windows) { static PREFIX: once_cell::sync::Lazy> = once_cell::sync::Lazy::new(|| { if let Some(root) = std::env::var_os("EXEPATH").map(PathBuf::from) { for candidate in ["mingw64", "mingw32"] { let candidate = root.join(candidate); if candidate.is_dir() { return Some(candidate); } } } let path = std::process::Command::new("git.exe") .arg("--exec-path") .stderr(std::process::Stdio::null()) .output() .ok()? .stdout; let path = BString::new(path) .trim_with(|b| b.is_ascii_whitespace()) .to_path() .ok()? .to_owned(); 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()) }); PREFIX.as_deref() } else { Path::new("/").into() } } /// Returns `$HOME` or `None` if it cannot be found. #[cfg(target_family = "wasm")] pub fn home_dir() -> Option { std::env::var("HOME").map(PathBuf::from).ok() } #[cfg(not(target_family = "wasm"))] pub use home::home_dir; /// Returns the contents of an environment variable of `name` with some special handling /// for certain environment variables (like `HOME`) for platform compatibility. pub fn var(name: &str) -> Option { if name == "HOME" { home_dir().map(PathBuf::into_os_string) } else { std::env::var_os(name) } }