gix_config/file/includes/
types.rs

1use std::path::PathBuf;
2
3use crate::{parse, path::interpolate};
4
5/// The error returned when following includes.
6#[derive(Debug, thiserror::Error)]
7#[allow(missing_docs)]
8pub enum Error {
9    #[error("Failed to copy configuration file into buffer")]
10    CopyBuffer(#[source] std::io::Error),
11    #[error("Could not read included configuration file at '{}'", path.display())]
12    Io { path: PathBuf, source: std::io::Error },
13    #[error(transparent)]
14    Parse(#[from] parse::Error),
15    #[error(transparent)]
16    Interpolate(#[from] interpolate::Error),
17    #[error("The maximum allowed length {} of the file include chain built by following nested resolve_includes is exceeded", .max_depth)]
18    IncludeDepthExceeded { max_depth: u8 },
19    #[error("Include paths from environment variables must not be relative as no config file paths exists as root")]
20    MissingConfigPath,
21    #[error("The git directory must be provided to support `gitdir:` conditional includes")]
22    MissingGitDir,
23    #[error(transparent)]
24    Realpath(#[from] gix_path::realpath::Error),
25}
26
27/// Options to handle includes, like `include.path` or `includeIf.<condition>.path`,
28#[derive(Clone, Copy)]
29pub struct Options<'a> {
30    /// The maximum allowed length of the file include chain built by following nested resolve_includes where base level is depth = 0.
31    pub max_depth: u8,
32    /// When max depth is exceeded while following nested includes,
33    /// return an error if true or silently stop following resolve_includes.
34    ///
35    /// Setting this value to false allows to read configuration with cycles,
36    /// which otherwise always results in an error.
37    pub err_on_max_depth_exceeded: bool,
38    /// If true, default false, failing to interpolate paths will result in an error.
39    ///
40    /// Interpolation also happens if paths in conditional includes can't be interpolated.
41    pub err_on_interpolation_failure: bool,
42    /// If true, default true, configuration not originating from a path will cause errors when trying to resolve
43    /// relative include paths (which would require the including configuration's path).
44    pub err_on_missing_config_path: bool,
45    /// Used during path interpolation, both for include paths before trying to read the file, and for
46    /// paths used in conditional `gitdir` includes.
47    pub interpolate: interpolate::Context<'a>,
48
49    /// Additional context for conditional includes to work.
50    pub conditional: conditional::Context<'a>,
51}
52
53impl<'a> Options<'a> {
54    /// Provide options to never follow include directives at all.
55    pub fn no_follow() -> Self {
56        Options {
57            max_depth: 0,
58            err_on_max_depth_exceeded: false,
59            err_on_interpolation_failure: false,
60            err_on_missing_config_path: false,
61            interpolate: Default::default(),
62            conditional: Default::default(),
63        }
64    }
65    /// Provide options to follow includes like git does, provided the required `conditional` and `interpolate` contexts
66    /// to support `gitdir` and `onbranch` based `includeIf` directives as well as standard `include.path` resolution.
67    /// Note that the follow-mode is `git`-style, following at most 10 indirections while
68    /// producing an error if the depth is exceeded.
69    pub fn follow(interpolate: interpolate::Context<'a>, conditional: conditional::Context<'a>) -> Self {
70        Options {
71            max_depth: 10,
72            err_on_max_depth_exceeded: true,
73            err_on_interpolation_failure: false,
74            err_on_missing_config_path: true,
75            interpolate,
76            conditional,
77        }
78    }
79
80    /// For use with `follow` type options, cause failure if an include path couldn't be interpolated or the depth limit is exceeded.
81    pub fn strict(mut self) -> Self {
82        self.err_on_interpolation_failure = true;
83        self.err_on_max_depth_exceeded = true;
84        self.err_on_missing_config_path = true;
85        self
86    }
87
88    /// Like [`follow`][Options::follow()], but without information to resolve `includeIf` directories as well as default
89    /// configuration to allow resolving `~username/` path. `home_dir` is required to resolve `~/` paths if set.
90    /// Note that `%(prefix)` paths cannot be interpolated with this configuration, use [`follow()`][Options::follow()]
91    /// instead for complete control.
92    pub fn follow_without_conditional(home_dir: Option<&'a std::path::Path>) -> Self {
93        Options {
94            max_depth: 10,
95            err_on_max_depth_exceeded: true,
96            err_on_interpolation_failure: false,
97            err_on_missing_config_path: true,
98            interpolate: interpolate::Context {
99                git_install_dir: None,
100                home_dir,
101                home_for_user: Some(interpolate::home_for_user),
102            },
103            conditional: Default::default(),
104        }
105    }
106
107    /// Set the context used for interpolation when interpolating paths to include as well as the paths
108    /// in `gitdir` conditional includes.
109    pub fn interpolate_with(mut self, context: interpolate::Context<'a>) -> Self {
110        self.interpolate = context;
111        self
112    }
113}
114
115impl Default for Options<'_> {
116    fn default() -> Self {
117        Self::no_follow()
118    }
119}
120
121///
122pub mod conditional {
123    /// Options to handle conditional includes like `includeIf.<condition>.path`.
124    #[derive(Clone, Copy, Default)]
125    pub struct Context<'a> {
126        /// The location of the .git directory. If `None`, `gitdir` conditions cause an error.
127        ///
128        /// Used for conditional includes, e.g. `includeIf.gitdir:…` or `includeIf:gitdir/i…`.
129        pub git_dir: Option<&'a std::path::Path>,
130        /// The name of the branch that is currently checked out. If `None`, `onbranch` conditions cause an error.
131        ///
132        /// Used for conditional includes, e.g. `includeIf.onbranch:main.…`
133        pub branch_name: Option<&'a gix_ref::FullNameRef>,
134    }
135}