summaryrefslogtreecommitdiffstats
path: root/vendor/gix-config/src/source.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-config/src/source.rs')
-rw-r--r--vendor/gix-config/src/source.rs163
1 files changed, 163 insertions, 0 deletions
diff --git a/vendor/gix-config/src/source.rs b/vendor/gix-config/src/source.rs
new file mode 100644
index 000000000..b1991e6b4
--- /dev/null
+++ b/vendor/gix-config/src/source.rs
@@ -0,0 +1,163 @@
+use std::{
+ borrow::Cow,
+ ffi::OsString,
+ path::{Path, PathBuf},
+};
+
+use crate::Source;
+
+/// The category of a [`Source`], in order of ascending precedence.
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
+pub enum Kind {
+ /// A special configuration file that ships with the git installation, and is thus tied to the used git binary.
+ GitInstallation,
+ /// A source shared for the entire system.
+ System,
+ /// Application specific configuration unique for each user of the `System`.
+ Global,
+ /// Configuration relevant only to the repository, possibly including the worktree.
+ Repository,
+ /// Configuration specified after all other configuration was loaded for the purpose of overrides.
+ Override,
+}
+
+impl Kind {
+ /// Return a list of sources associated with this `Kind` of source, in order of ascending precedence.
+ pub fn sources(self) -> &'static [Source] {
+ let src = match self {
+ Kind::GitInstallation => &[Source::GitInstallation] as &[_],
+ Kind::System => &[Source::System],
+ Kind::Global => &[Source::Git, Source::User],
+ Kind::Repository => &[Source::Local, Source::Worktree],
+ Kind::Override => &[Source::Env, Source::Cli, Source::Api],
+ };
+ debug_assert!(
+ src.iter().all(|src| src.kind() == self),
+ "BUG: classification of source has to match the ordering here, see `Source::kind()`"
+ );
+ src
+ }
+}
+
+impl Source {
+ /// Return true if the source indicates a location within a file of a repository.
+ pub const fn kind(self) -> Kind {
+ use Source::*;
+ match self {
+ GitInstallation => Kind::GitInstallation,
+ System => Kind::System,
+ Git | User => Kind::Global,
+ Local | Worktree => Kind::Repository,
+ Env | Cli | Api | EnvOverride => Kind::Override,
+ }
+ }
+
+ /// Returns the location at which a file of this type would be stored, or `None` if
+ /// there is no notion of persistent storage for this source, with `env_var` to obtain environment variables.
+ /// Note that the location can be relative for repository-local sources like `Local` and `Worktree`,
+ /// and the caller has to known which base it it relative to, namely the `common_dir` in the `Local` case
+ /// and the `git_dir` in the `Worktree` case.
+ /// Be aware that depending on environment overrides, multiple scopes might return the same path, which should
+ /// only be loaded once nonetheless.
+ ///
+ /// With `env_var` it becomes possible to prevent accessing environment variables entirely to comply with `gix-sec`
+ /// permissions for example.
+ pub fn storage_location(self, env_var: &mut dyn FnMut(&str) -> Option<OsString>) -> Option<Cow<'static, Path>> {
+ use Source::*;
+ match self {
+ GitInstallation => git::install_config_path().map(gix_path::from_bstr),
+ System => env_var("GIT_CONFIG_NO_SYSTEM")
+ .is_none()
+ .then(|| PathBuf::from(env_var("GIT_CONFIG_SYSTEM").unwrap_or_else(|| "/etc/gitconfig".into())).into()),
+ Git => match env_var("GIT_CONFIG_GLOBAL") {
+ Some(global_override) => Some(PathBuf::from(global_override).into()),
+ None => env_var("XDG_CONFIG_HOME")
+ .map(|home| {
+ let mut p = PathBuf::from(home);
+ p.push("git");
+ p.push("config");
+ p
+ })
+ .or_else(|| {
+ env_var("HOME").map(|home| {
+ let mut p = PathBuf::from(home);
+ p.push(".config");
+ p.push("git");
+ p.push("config");
+ p
+ })
+ })
+ .map(Cow::Owned),
+ },
+ User => env_var("GIT_CONFIG_GLOBAL")
+ .map(|global_override| PathBuf::from(global_override).into())
+ .or_else(|| {
+ env_var("HOME").map(|home| {
+ let mut p = PathBuf::from(home);
+ p.push(".gitconfig");
+ p.into()
+ })
+ }),
+ Local => Some(Path::new("config").into()),
+ Worktree => Some(Path::new("config.worktree").into()),
+ Env | Cli | Api | EnvOverride => None,
+ }
+ }
+}
+
+/// Environment information involving the `git` program itself.
+mod git {
+ use std::process::{Command, Stdio};
+
+ use bstr::{BStr, BString, ByteSlice};
+
+ /// Returns the file that contains git configuration coming with the installation of the `git` file in the current `PATH`, or `None`
+ /// if no `git` executable was found or there were other errors during execution.
+ pub fn install_config_path() -> Option<&'static BStr> {
+ static PATH: once_cell::sync::Lazy<Option<BString>> = once_cell::sync::Lazy::new(|| {
+ let mut cmd = Command::new(if cfg!(windows) { "git.exe" } else { "git" });
+ cmd.args(["config", "-l", "--show-origin"])
+ .stdin(Stdio::null())
+ .stderr(Stdio::null());
+ first_file_from_config_with_origin(cmd.output().ok()?.stdout.as_slice().into()).map(ToOwned::to_owned)
+ });
+ PATH.as_ref().map(|b| b.as_ref())
+ }
+
+ fn first_file_from_config_with_origin(source: &BStr) -> Option<&BStr> {
+ let file = source.strip_prefix(b"file:")?;
+ let end_pos = file.find_byte(b'\t')?;
+ file[..end_pos].as_bstr().into()
+ }
+
+ #[cfg(test)]
+ mod tests {
+ #[test]
+ fn first_file_from_config_with_origin() {
+ let macos = "file:/Applications/Xcode.app/Contents/Developer/usr/share/git-core/gitconfig credential.helper=osxkeychain\nfile:/Users/byron/.gitconfig push.default=simple\n";
+ let win_msys =
+ "file:C:/git-sdk-64/etc/gitconfig core.symlinks=false\r\nfile:C:/git-sdk-64/etc/gitconfig core.autocrlf=true";
+ let win_cmd = "file:C:/Program Files/Git/etc/gitconfig diff.astextplain.textconv=astextplain\r\nfile:C:/Program Files/Git/etc/gitconfig filter.lfs.clean=gix-lfs clean -- %f\r\n";
+ let linux = "file:/home/parallels/.gitconfig core.excludesfile=~/.gitignore\n";
+ let bogus = "something unexpected";
+ let empty = "";
+
+ for (source, expected) in [
+ (
+ macos,
+ Some("/Applications/Xcode.app/Contents/Developer/usr/share/git-core/gitconfig"),
+ ),
+ (win_msys, Some("C:/git-sdk-64/etc/gitconfig")),
+ (win_cmd, Some("C:/Program Files/Git/etc/gitconfig")),
+ (linux, Some("/home/parallels/.gitconfig")),
+ (bogus, None),
+ (empty, None),
+ ] {
+ assert_eq!(
+ super::first_file_from_config_with_origin(source.into()),
+ expected.map(Into::into)
+ );
+ }
+ }
+ }
+}