Skip to main content

gix_config/file/init/
comfort.rs

1use crate::{
2    File, Source,
3    file::{Metadata, init},
4    path, source,
5};
6
7/// Easy-instantiation of typical non-repository git configuration files with all configuration defaulting to typical values.
8///
9/// ### Limitations
10///
11/// Note that `includeIf` conditions in global files will cause failure as the required information
12/// to resolve them isn't present without a repository.
13///
14/// Also note that relevant information to interpolate paths will be obtained from the environment or other
15/// source on unix.
16impl File {
17    /// Open all global configuration files which involves the following sources:
18    ///
19    /// * [git-installation](source::Kind::GitInstallation)
20    /// * [system](source::Kind::System)
21    /// * [globals](source::Kind::Global)
22    ///
23    /// which excludes repository local configuration, as well as override-configuration from environment variables.
24    ///
25    /// Note that the file might [be empty][File::is_void()] in case no configuration file was found.
26    pub fn from_globals() -> Result<File, init::from_paths::Error> {
27        let metas = [
28            source::Kind::GitInstallation,
29            source::Kind::System,
30            source::Kind::Global,
31        ]
32        .iter()
33        .flat_map(|kind| kind.sources())
34        .filter_map(|source| {
35            let path = source
36                .storage_location(&mut gix_path::env::var)
37                .and_then(|p| p.is_file().then_some(p));
38
39            Metadata {
40                path,
41                source: *source,
42                level: 0,
43                trust: gix_sec::Trust::Full,
44            }
45            .into()
46        });
47
48        let home = gix_path::env::home_dir();
49        let options = init::Options {
50            includes: init::includes::Options::follow_without_conditional(home.as_deref()),
51            ..Default::default()
52        };
53        File::from_paths_metadata(metas, options).map(Option::unwrap_or_default)
54    }
55
56    /// Generates a config from `GIT_CONFIG_*` environment variables and return a possibly empty `File`.
57    /// A typical use of this is to [`append`][File::append()] this configuration to another one with lower
58    /// precedence to obtain overrides.
59    ///
60    /// See [`git-config`'s documentation] for more information on the environment variables in question.
61    ///
62    /// [`git-config`'s documentation]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-GITCONFIGCOUNT
63    pub fn from_environment_overrides() -> Result<File, init::from_env::Error> {
64        let home = gix_path::env::home_dir();
65        let options = init::Options {
66            includes: init::includes::Options::follow_without_conditional(home.as_deref()),
67            ..Default::default()
68        };
69
70        File::from_env(options).map(Option::unwrap_or_default)
71    }
72}
73
74/// An easy way to provide complete configuration for a repository.
75impl File {
76    /// This configuration type includes the following sources, in order of precedence:
77    ///
78    /// - globals
79    /// - repository-local by loading `dir`/config
80    /// - worktree by loading `dir`/config.worktree
81    /// - environment
82    ///
83    /// Note that `dir` is the `.git` dir to load the configuration from, not the configuration file.
84    ///
85    /// Includes will be resolved within limits as some information like the git installation directory is missing to interpolate
86    /// paths with as well as git repository information like the branch name.
87    pub fn from_git_dir(dir: std::path::PathBuf) -> Result<File, from_git_dir::Error> {
88        let (mut local, git_dir) = {
89            let source = Source::Local;
90            let mut path = dir;
91            path.push(
92                source
93                    .storage_location(&mut gix_path::env::var)
94                    .expect("location available for local"),
95            );
96            let local = Self::from_path_no_includes(path.clone(), source)?;
97            path.pop();
98            (local, path)
99        };
100
101        let worktree = match local.boolean("extensions.worktreeConfig") {
102            Ok(Some(worktree_config)) => worktree_config.then(|| {
103                let source = Source::Worktree;
104                let path = git_dir.join(
105                    source
106                        .storage_location(&mut gix_path::env::var)
107                        .expect("location available for worktree"),
108                );
109                Self::from_path_no_includes(path, source)
110            }),
111            _ => None,
112        }
113        .transpose()?;
114
115        let home = gix_path::env::home_dir();
116        let options = init::Options {
117            includes: init::includes::Options::follow(
118                path::interpolate::Context {
119                    home_dir: home.as_deref(),
120                    ..Default::default()
121                },
122                init::includes::conditional::Context {
123                    git_dir: Some(git_dir.as_ref()),
124                    branch_name: None,
125                },
126            ),
127            ..Default::default()
128        };
129
130        let mut globals = Self::from_globals()?;
131        globals.resolve_includes(options)?;
132        local.resolve_includes(options)?;
133
134        globals.append(local)?;
135        if let Some(mut worktree) = worktree {
136            worktree.resolve_includes(options)?;
137            globals.append(worktree)?;
138        }
139        globals.append(Self::from_environment_overrides()?)?;
140
141        Ok(globals)
142    }
143}
144
145///
146pub mod from_git_dir {
147    use crate::file::init;
148
149    /// The error returned by [`File::from_git_dir()`][crate::File::from_git_dir()].
150    #[derive(Debug, thiserror::Error)]
151    pub enum Error {
152        #[error(transparent)]
153        FromPaths(#[from] init::from_paths::Error),
154        #[error(transparent)]
155        FromEnv(#[from] init::from_env::Error),
156        #[error(transparent)]
157        Init(#[from] init::Error),
158        #[error(transparent)]
159        Includes(#[from] init::includes::Error),
160        #[error(transparent)]
161        Span(#[from] crate::parse::span::Error),
162    }
163}