gix_config/file/init/
comfort.rs

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