summaryrefslogtreecommitdiffstats
path: root/vendor/gix-config/src/file/init
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-config/src/file/init')
-rw-r--r--vendor/gix-config/src/file/init/comfort.rs159
-rw-r--r--vendor/gix-config/src/file/init/from_env.rs88
-rw-r--r--vendor/gix-config/src/file/init/from_paths.rs94
-rw-r--r--vendor/gix-config/src/file/init/mod.rs86
-rw-r--r--vendor/gix-config/src/file/init/types.rs47
5 files changed, 474 insertions, 0 deletions
diff --git a/vendor/gix-config/src/file/init/comfort.rs b/vendor/gix-config/src/file/init/comfort.rs
new file mode 100644
index 000000000..ffe859a1a
--- /dev/null
+++ b/vendor/gix-config/src/file/init/comfort.rs
@@ -0,0 +1,159 @@
+use std::path::PathBuf;
+
+use crate::{
+ file::{init, Metadata},
+ path, source, File, Source,
+};
+
+/// Easy-instantiation of typical non-repository git configuration files with all configuration defaulting to typical values.
+///
+/// ### Limitations
+///
+/// Note that `includeIf` conditions in global files will cause failure as the required information
+/// to resolve them isn't present without a repository.
+///
+/// Also note that relevant information to interpolate paths will be obtained from the environment or other
+/// source on unix.
+impl File<'static> {
+ /// Open all global configuration files which involves the following sources:
+ ///
+ /// * [system][crate::Source::System]
+ /// * [git][crate::Source::Git]
+ /// * [user][crate::Source::User]
+ ///
+ /// which excludes repository local configuration, as well as override-configuration from environment variables.
+ ///
+ /// Note that the file might [be empty][File::is_void()] in case no configuration file was found.
+ pub fn from_globals() -> Result<File<'static>, init::from_paths::Error> {
+ let metas = [source::Kind::System, source::Kind::Global]
+ .iter()
+ .flat_map(|kind| kind.sources())
+ .filter_map(|source| {
+ let path = source
+ .storage_location(&mut |name| std::env::var_os(name))
+ .and_then(|p| p.is_file().then_some(p))
+ .map(|p| p.into_owned());
+
+ Metadata {
+ path,
+ source: *source,
+ level: 0,
+ trust: gix_sec::Trust::Full,
+ }
+ .into()
+ });
+
+ let home = std::env::var("HOME").ok().map(PathBuf::from);
+ let options = init::Options {
+ includes: init::includes::Options::follow_without_conditional(home.as_deref()),
+ ..Default::default()
+ };
+ File::from_paths_metadata(metas, options).map(Option::unwrap_or_default)
+ }
+
+ /// Generates a config from `GIT_CONFIG_*` environment variables and return a possibly empty `File`.
+ /// A typical use of this is to [`append`][File::append()] this configuration to another one with lower
+ /// precedence to obtain overrides.
+ ///
+ /// See [`gix-config`'s documentation] for more information on the environment variables in question.
+ ///
+ /// [`gix-config`'s documentation]: https://git-scm.com/docs/gix-config#Documentation/gix-config.txt-GITCONFIGCOUNT
+ pub fn from_environment_overrides() -> Result<File<'static>, init::from_env::Error> {
+ let home = std::env::var("HOME").ok().map(PathBuf::from);
+ let options = init::Options {
+ includes: init::includes::Options::follow_without_conditional(home.as_deref()),
+ ..Default::default()
+ };
+
+ File::from_env(options).map(Option::unwrap_or_default)
+ }
+}
+
+/// An easy way to provide complete configuration for a repository.
+impl File<'static> {
+ /// This configuration type includes the following sources, in order of precedence:
+ ///
+ /// - globals
+ /// - repository-local by loading `dir`/config
+ /// - worktree by loading `dir`/config.worktree
+ /// - environment
+ ///
+ /// Note that `dir` is the `.git` dir to load the configuration from, not the configuration file.
+ ///
+ /// Includes will be resolved within limits as some information like the git installation directory is missing to interpolate
+ /// paths with as well as git repository information like the branch name.
+ pub fn from_git_dir(dir: impl Into<std::path::PathBuf>) -> Result<File<'static>, from_git_dir::Error> {
+ let (mut local, git_dir) = {
+ let source = Source::Local;
+ let mut path = dir.into();
+ path.push(
+ source
+ .storage_location(&mut |n| std::env::var_os(n))
+ .expect("location available for local"),
+ );
+ let local = Self::from_path_no_includes(&path, source)?;
+ path.pop();
+ (local, path)
+ };
+
+ let worktree = match local.boolean("extensions", None, "worktreeConfig") {
+ Some(Ok(worktree_config)) => worktree_config.then(|| {
+ let source = Source::Worktree;
+ let path = git_dir.join(
+ source
+ .storage_location(&mut |n| std::env::var_os(n))
+ .expect("location available for worktree"),
+ );
+ Self::from_path_no_includes(path, source)
+ }),
+ _ => None,
+ }
+ .transpose()?;
+
+ let home = std::env::var("HOME").ok().map(PathBuf::from);
+ let options = init::Options {
+ includes: init::includes::Options::follow(
+ path::interpolate::Context {
+ home_dir: home.as_deref(),
+ ..Default::default()
+ },
+ init::includes::conditional::Context {
+ git_dir: Some(git_dir.as_ref()),
+ branch_name: None,
+ },
+ ),
+ lossy: false,
+ };
+
+ let mut globals = Self::from_globals()?;
+ globals.resolve_includes(options)?;
+ local.resolve_includes(options)?;
+
+ globals.append(local);
+ if let Some(mut worktree) = worktree {
+ worktree.resolve_includes(options)?;
+ globals.append(worktree);
+ }
+ globals.append(Self::from_environment_overrides()?);
+
+ Ok(globals)
+ }
+}
+
+///
+pub mod from_git_dir {
+ use crate::file::init;
+
+ /// The error returned by [`File::from_git_dir()`][crate::File::from_git_dir()].
+ #[derive(Debug, thiserror::Error)]
+ pub enum Error {
+ #[error(transparent)]
+ FromPaths(#[from] init::from_paths::Error),
+ #[error(transparent)]
+ FromEnv(#[from] init::from_env::Error),
+ #[error(transparent)]
+ Init(#[from] init::Error),
+ #[error(transparent)]
+ Includes(#[from] init::includes::Error),
+ }
+}
diff --git a/vendor/gix-config/src/file/init/from_env.rs b/vendor/gix-config/src/file/init/from_env.rs
new file mode 100644
index 000000000..167d37399
--- /dev/null
+++ b/vendor/gix-config/src/file/init/from_env.rs
@@ -0,0 +1,88 @@
+use std::convert::TryFrom;
+
+use bstr::{BStr, ByteSlice};
+
+use crate::{file, file::init, parse, parse::section, path::interpolate, File};
+
+/// Represents the errors that may occur when calling [`File::from_env()`].
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error("Configuration {kind} at index {index} contained illformed UTF-8")]
+ IllformedUtf8 { index: usize, kind: &'static str },
+ #[error("GIT_CONFIG_COUNT was not a positive integer: {}", .input)]
+ InvalidConfigCount { input: String },
+ #[error("GIT_CONFIG_KEY_{} was not set", .key_id)]
+ InvalidKeyId { key_id: usize },
+ #[error("GIT_CONFIG_KEY_{} was set to an invalid value: {}", .key_id, .key_val)]
+ InvalidKeyValue { key_id: usize, key_val: String },
+ #[error("GIT_CONFIG_VALUE_{} was not set", .value_id)]
+ InvalidValueId { value_id: usize },
+ #[error(transparent)]
+ PathInterpolationError(#[from] interpolate::Error),
+ #[error(transparent)]
+ Includes(#[from] init::includes::Error),
+ #[error(transparent)]
+ Section(#[from] section::header::Error),
+ #[error(transparent)]
+ Key(#[from] section::key::Error),
+}
+
+/// Instantiation from environment variables
+impl File<'static> {
+ /// Generates a config from `GIT_CONFIG_*` environment variables or returns `Ok(None)` if no configuration was found.
+ /// See [`gix-config`'s documentation] for more information on the environment variables in question.
+ ///
+ /// With `options` configured, it's possible to resolve `include.path` or `includeIf.<condition>.path` directives as well.
+ ///
+ /// [`gix-config`'s documentation]: https://git-scm.com/docs/gix-config#Documentation/gix-config.txt-GITCONFIGCOUNT
+ pub fn from_env(options: init::Options<'_>) -> Result<Option<File<'static>>, Error> {
+ use std::env;
+ let count: usize = match env::var("GIT_CONFIG_COUNT") {
+ Ok(v) => v.parse().map_err(|_| Error::InvalidConfigCount { input: v })?,
+ Err(_) => return Ok(None),
+ };
+
+ if count == 0 {
+ return Ok(None);
+ }
+
+ let meta = file::Metadata {
+ path: None,
+ source: crate::Source::Env,
+ level: 0,
+ trust: gix_sec::Trust::Full,
+ };
+ let mut config = File::new(meta);
+ for i in 0..count {
+ let key = gix_path::os_string_into_bstring(
+ env::var_os(format!("GIT_CONFIG_KEY_{i}")).ok_or(Error::InvalidKeyId { key_id: i })?,
+ )
+ .map_err(|_| Error::IllformedUtf8 { index: i, kind: "key" })?;
+ let value = env::var_os(format!("GIT_CONFIG_VALUE_{i}")).ok_or(Error::InvalidValueId { value_id: i })?;
+ let key = parse::key(<_ as AsRef<BStr>>::as_ref(&key)).ok_or_else(|| Error::InvalidKeyValue {
+ key_id: i,
+ key_val: key.to_string(),
+ })?;
+
+ config
+ .section_mut_or_create_new(key.section_name, key.subsection_name)?
+ .push(
+ section::Key::try_from(key.value_name.to_owned())?,
+ Some(
+ gix_path::os_str_into_bstr(&value)
+ .map_err(|_| Error::IllformedUtf8 {
+ index: i,
+ kind: "value",
+ })?
+ .as_bytes()
+ .into(),
+ ),
+ );
+ }
+
+ let mut buf = Vec::new();
+ init::includes::resolve(&mut config, &mut buf, options)?;
+ Ok(Some(config))
+ }
+}
diff --git a/vendor/gix-config/src/file/init/from_paths.rs b/vendor/gix-config/src/file/init/from_paths.rs
new file mode 100644
index 000000000..5d671b69e
--- /dev/null
+++ b/vendor/gix-config/src/file/init/from_paths.rs
@@ -0,0 +1,94 @@
+use std::collections::BTreeSet;
+
+use crate::{
+ file::{init, init::Options, Metadata},
+ File,
+};
+
+/// The error returned by [`File::from_paths_metadata()`] and [`File::from_path_no_includes()`].
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error(transparent)]
+ Io(#[from] std::io::Error),
+ #[error(transparent)]
+ Init(#[from] init::Error),
+}
+
+/// Instantiation from one or more paths
+impl File<'static> {
+ /// Load the single file at `path` with `source` without following include directives.
+ ///
+ /// Note that the path will be checked for ownership to derive trust.
+ pub fn from_path_no_includes(path: impl Into<std::path::PathBuf>, source: crate::Source) -> Result<Self, Error> {
+ let path = path.into();
+ let trust = gix_sec::Trust::from_path_ownership(&path)?;
+
+ let mut buf = Vec::new();
+ std::io::copy(&mut std::fs::File::open(&path)?, &mut buf)?;
+
+ Ok(File::from_bytes_owned(
+ &mut buf,
+ Metadata::from(source).at(path).with(trust),
+ Default::default(),
+ )?)
+ }
+
+ /// Constructs a `gix-config` file from the provided metadata, which must include a path to read from or be ignored.
+ /// Returns `Ok(None)` if there was not a single input path provided, which is a possibility due to
+ /// [`Metadata::path`] being an `Option`.
+ /// If an input path doesn't exist, the entire operation will abort. See [`from_paths_metadata_buf()`][Self::from_paths_metadata_buf()]
+ /// for a more powerful version of this method.
+ pub fn from_paths_metadata(
+ path_meta: impl IntoIterator<Item = impl Into<Metadata>>,
+ options: Options<'_>,
+ ) -> Result<Option<Self>, Error> {
+ let mut buf = Vec::with_capacity(512);
+ let err_on_nonexisting_paths = true;
+ Self::from_paths_metadata_buf(path_meta, &mut buf, err_on_nonexisting_paths, options)
+ }
+
+ /// Like [from_paths_metadata()][Self::from_paths_metadata()], but will use `buf` to temporarily store the config file
+ /// contents for parsing instead of allocating an own buffer.
+ ///
+ /// If `err_on_nonexisting_paths` is false, instead of aborting with error, we will continue to the next path instead.
+ pub fn from_paths_metadata_buf(
+ path_meta: impl IntoIterator<Item = impl Into<Metadata>>,
+ buf: &mut Vec<u8>,
+ err_on_non_existing_paths: bool,
+ options: Options<'_>,
+ ) -> Result<Option<Self>, Error> {
+ let mut target = None;
+ let mut seen = BTreeSet::default();
+ for (path, mut meta) in path_meta.into_iter().filter_map(|meta| {
+ let mut meta = meta.into();
+ meta.path.take().map(|p| (p, meta))
+ }) {
+ if !seen.insert(path.clone()) {
+ continue;
+ }
+
+ buf.clear();
+ std::io::copy(
+ &mut match std::fs::File::open(&path) {
+ Ok(f) => f,
+ Err(err) if !err_on_non_existing_paths && err.kind() == std::io::ErrorKind::NotFound => continue,
+ Err(err) => return Err(err.into()),
+ },
+ buf,
+ )?;
+ meta.path = Some(path);
+
+ let config = Self::from_bytes_owned(buf, meta, options)?;
+ match &mut target {
+ None => {
+ target = Some(config);
+ }
+ Some(target) => {
+ target.append(config);
+ }
+ }
+ }
+ Ok(target)
+ }
+}
diff --git a/vendor/gix-config/src/file/init/mod.rs b/vendor/gix-config/src/file/init/mod.rs
new file mode 100644
index 000000000..5b4030241
--- /dev/null
+++ b/vendor/gix-config/src/file/init/mod.rs
@@ -0,0 +1,86 @@
+use gix_features::threading::OwnShared;
+
+use crate::{
+ file::{includes, section, Metadata},
+ parse, File,
+};
+
+mod types;
+pub use types::{Error, Options};
+
+mod comfort;
+///
+pub mod from_env;
+///
+pub mod from_paths;
+
+impl<'a> File<'a> {
+ /// Return an empty `File` with the given `meta`-data to be attached to all new sections.
+ pub fn new(meta: impl Into<OwnShared<Metadata>>) -> Self {
+ Self {
+ frontmatter_events: Default::default(),
+ frontmatter_post_section: Default::default(),
+ section_lookup_tree: Default::default(),
+ sections: Default::default(),
+ section_id_counter: 0,
+ section_order: Default::default(),
+ meta: meta.into(),
+ }
+ }
+
+ /// Instantiate a new `File` from given `input`, associating each section and their values with
+ /// `meta`-data, while respecting `options`.
+ pub fn from_bytes_no_includes(
+ input: &'a [u8],
+ meta: impl Into<OwnShared<Metadata>>,
+ options: Options<'_>,
+ ) -> Result<Self, Error> {
+ let meta = meta.into();
+ Ok(Self::from_parse_events_no_includes(
+ parse::Events::from_bytes(input, options.to_event_filter())?,
+ meta,
+ ))
+ }
+
+ /// Instantiate a new `File` from given `events`, associating each section and their values with
+ /// `meta`-data.
+ pub fn from_parse_events_no_includes(
+ parse::Events { frontmatter, sections }: parse::Events<'a>,
+ meta: impl Into<OwnShared<Metadata>>,
+ ) -> Self {
+ let meta = meta.into();
+ let mut this = File::new(OwnShared::clone(&meta));
+
+ this.frontmatter_events = frontmatter;
+
+ for section in sections {
+ this.push_section_internal(crate::file::Section {
+ header: section.header,
+ body: section::Body(section.events),
+ meta: OwnShared::clone(&meta),
+ id: Default::default(),
+ });
+ }
+
+ this
+ }
+}
+
+impl File<'static> {
+ /// Instantiate a new fully-owned `File` from given `input` (later reused as buffer when resolving includes),
+ /// associating each section and their values with `meta`-data, while respecting `options`, and
+ /// following includes as configured there.
+ pub fn from_bytes_owned(
+ input_and_buf: &mut Vec<u8>,
+ meta: impl Into<OwnShared<Metadata>>,
+ options: Options<'_>,
+ ) -> Result<Self, Error> {
+ let mut config = Self::from_parse_events_no_includes(
+ parse::Events::from_bytes_owned(input_and_buf, options.to_event_filter()).map_err(Error::from)?,
+ meta,
+ );
+
+ includes::resolve(&mut config, input_and_buf, options).map_err(Error::from)?;
+ Ok(config)
+ }
+}
diff --git a/vendor/gix-config/src/file/init/types.rs b/vendor/gix-config/src/file/init/types.rs
new file mode 100644
index 000000000..fcb17c0ca
--- /dev/null
+++ b/vendor/gix-config/src/file/init/types.rs
@@ -0,0 +1,47 @@
+use crate::{file::init, parse, parse::Event, path::interpolate};
+
+/// The error returned by [`File::from_bytes_no_includes()`][crate::File::from_bytes_no_includes()].
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error(transparent)]
+ Parse(#[from] parse::Error),
+ #[error(transparent)]
+ Interpolate(#[from] interpolate::Error),
+ #[error(transparent)]
+ Includes(#[from] init::includes::Error),
+}
+
+/// Options when loading git config using [`File::from_paths_metadata()`][crate::File::from_paths_metadata()].
+#[derive(Clone, Copy, Default)]
+pub struct Options<'a> {
+ /// Configure how to follow includes while handling paths.
+ pub includes: init::includes::Options<'a>,
+ /// If true, only value-bearing parse events will be kept to reduce memory usage and increase performance.
+ ///
+ /// Note that doing so will degenerate [`write_to()`][crate::File::write_to()] and strip it off its comments
+ /// and additional whitespace entirely, but will otherwise be a valid configuration file.
+ pub lossy: bool,
+}
+
+impl Options<'_> {
+ pub(crate) fn to_event_filter(self) -> Option<fn(&Event<'_>) -> bool> {
+ if self.lossy {
+ Some(discard_nonessential_events)
+ } else {
+ None
+ }
+ }
+}
+
+fn discard_nonessential_events(e: &Event<'_>) -> bool {
+ match e {
+ Event::Whitespace(_) | Event::Comment(_) | Event::Newline(_) => false,
+ Event::SectionHeader(_)
+ | Event::SectionKey(_)
+ | Event::KeyValueSeparator
+ | Event::Value(_)
+ | Event::ValueNotDone(_)
+ | Event::ValueDone(_) => true,
+ }
+}