gix_config/source.rs
1use std::{ffi::OsString, path::PathBuf};
2
3use crate::Source;
4
5/// The category of a [`Source`], in order of ascending precedence.
6#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
7pub enum Kind {
8 /// A special configuration file that ships with the git installation, and is thus tied to the used git binary.
9 GitInstallation,
10 /// A source shared for the entire system.
11 System,
12 /// Application specific configuration unique for each user of the `System`.
13 Global,
14 /// Configuration relevant only to the repository, possibly including the worktree.
15 Repository,
16 /// Configuration specified after all other configuration was loaded for the purpose of overrides.
17 Override,
18}
19
20impl Kind {
21 /// Return a list of sources associated with this `Kind` of source, in order of ascending precedence.
22 pub fn sources(self) -> &'static [Source] {
23 let src = match self {
24 Kind::GitInstallation => &[Source::GitInstallation] as &[_],
25 Kind::System => &[Source::System],
26 Kind::Global => &[Source::Git, Source::User],
27 Kind::Repository => &[Source::Local, Source::Worktree],
28 Kind::Override => &[Source::Env, Source::Cli, Source::Api],
29 };
30 debug_assert!(
31 src.iter().all(|src| src.kind() == self),
32 "BUG: classification of source has to match the ordering here, see `Source::kind()`"
33 );
34 src
35 }
36}
37
38impl Source {
39 /// Return true if the source indicates a location within a file of a repository.
40 pub const fn kind(self) -> Kind {
41 use Source::*;
42 match self {
43 GitInstallation => Kind::GitInstallation,
44 System => Kind::System,
45 Git | User => Kind::Global,
46 Local | Worktree => Kind::Repository,
47 Env | Cli | Api | EnvOverride => Kind::Override,
48 }
49 }
50
51 /// Returns the location at which a file of this type would be stored, or `None` if
52 /// there is no notion of persistent storage for this source, with `env_var` to obtain environment variables.
53 /// Note that the location can be relative for repository-local sources like `Local` and `Worktree`,
54 /// and the caller has to known which base it is relative to, namely the `common_dir` in the `Local` case
55 /// and the `git_dir` in the `Worktree` case.
56 /// Be aware that depending on environment overrides, multiple scopes might return the same path, which should
57 /// only be loaded once nonetheless.
58 ///
59 /// With `env_var` it becomes possible to prevent accessing environment variables entirely to comply with `gix-sec`
60 /// permissions for example.
61 pub fn storage_location(self, env_var: &mut dyn FnMut(&str) -> Option<OsString>) -> Option<PathBuf> {
62 use Source::*;
63 match self {
64 GitInstallation => {
65 if env_var("GIT_CONFIG_NOSYSTEM")
66 .map(crate::Boolean::try_from)
67 .transpose()
68 .ok()
69 .flatten()
70 .is_some_and(|b| b.0)
71 {
72 None
73 } else {
74 gix_path::env::installation_config().map(Into::into)
75 }
76 }
77 System => {
78 if env_var("GIT_CONFIG_NOSYSTEM")
79 .map(crate::Boolean::try_from)
80 .transpose()
81 .ok()
82 .flatten()
83 .is_some_and(|b| b.0)
84 {
85 None
86 } else {
87 env_var("GIT_CONFIG_SYSTEM")
88 .map(Into::into)
89 .or_else(|| gix_path::env::system_prefix().map(|p| p.join("etc/gitconfig")))
90 }
91 }
92 Git => match env_var("GIT_CONFIG_GLOBAL") {
93 Some(global_override) => Some(PathBuf::from(global_override)),
94 None => gix_path::env::xdg_config("config", env_var),
95 },
96 User => env_var("GIT_CONFIG_GLOBAL").map(PathBuf::from).or_else(|| {
97 env_var("HOME")
98 .map(PathBuf::from)
99 .or_else(|| {
100 if cfg!(windows) {
101 // On Windows, HOME is rarely set, and we generally need something more.
102 std::env::home_dir()
103 } else {
104 // Git also only tries the env var on unix, and so do we
105 None
106 }
107 })
108 .map(|mut p| {
109 p.push(".gitconfig");
110 p
111 })
112 }),
113 Local => Some("config".into()),
114 Worktree => Some("config.worktree".into()),
115 Env | Cli | Api | EnvOverride => None,
116 }
117 }
118}