Skip to main content

gix_config/file/init/
comfort.rs

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