summaryrefslogtreecommitdiffstats
path: root/vendor/gix-config/src/file/access
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-config/src/file/access
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz
rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-config/src/file/access')
-rw-r--r--vendor/gix-config/src/file/access/comfort.rs274
-rw-r--r--vendor/gix-config/src/file/access/mod.rs4
-rw-r--r--vendor/gix-config/src/file/access/mutate.rs387
-rw-r--r--vendor/gix-config/src/file/access/raw.rs536
-rw-r--r--vendor/gix-config/src/file/access/read_only.rs353
5 files changed, 1554 insertions, 0 deletions
diff --git a/vendor/gix-config/src/file/access/comfort.rs b/vendor/gix-config/src/file/access/comfort.rs
new file mode 100644
index 000000000..b4953c597
--- /dev/null
+++ b/vendor/gix-config/src/file/access/comfort.rs
@@ -0,0 +1,274 @@
+use std::{borrow::Cow, convert::TryFrom};
+
+use bstr::BStr;
+
+use crate::{file::MetadataFilter, value, File};
+
+/// Comfortable API for accessing values
+impl<'event> File<'event> {
+ /// Like [`value()`][File::value()], but returning `None` if the string wasn't found.
+ ///
+ /// As strings perform no conversions, this will never fail.
+ pub fn string(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ ) -> Option<Cow<'_, BStr>> {
+ self.string_filter(section_name, subsection_name, key, &mut |_| true)
+ }
+
+ /// Like [`string()`][File::string()], but suitable for statically known `key`s like `remote.origin.url`.
+ pub fn string_by_key<'a>(&self, key: impl Into<&'a BStr>) -> Option<Cow<'_, BStr>> {
+ self.string_filter_by_key(key, &mut |_| true)
+ }
+
+ /// Like [`string()`][File::string()], but the section containing the returned value must pass `filter` as well.
+ pub fn string_filter(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ filter: &mut MetadataFilter,
+ ) -> Option<Cow<'_, BStr>> {
+ self.raw_value_filter(section_name, subsection_name, key, filter).ok()
+ }
+
+ /// Like [`string_filter()`][File::string_filter()], but suitable for statically known `key`s like `remote.origin.url`.
+ pub fn string_filter_by_key<'a>(
+ &self,
+ key: impl Into<&'a BStr>,
+ filter: &mut MetadataFilter,
+ ) -> Option<Cow<'_, BStr>> {
+ let key = crate::parse::key(key)?;
+ self.raw_value_filter(key.section_name, key.subsection_name, key.value_name, filter)
+ .ok()
+ }
+
+ /// Like [`value()`][File::value()], but returning `None` if the path wasn't found.
+ ///
+ /// Note that this path is not vetted and should only point to resources which can't be used
+ /// to pose a security risk. Prefer using [`path_filter()`][File::path_filter()] instead.
+ ///
+ /// As paths perform no conversions, this will never fail.
+ pub fn path(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ ) -> Option<crate::Path<'_>> {
+ self.path_filter(section_name, subsection_name, key, &mut |_| true)
+ }
+
+ /// Like [`path()`][File::path()], but suitable for statically known `key`s like `remote.origin.url`.
+ pub fn path_by_key<'a>(&self, key: impl Into<&'a BStr>) -> Option<crate::Path<'_>> {
+ self.path_filter_by_key(key, &mut |_| true)
+ }
+
+ /// Like [`path()`][File::path()], but the section containing the returned value must pass `filter` as well.
+ ///
+ /// This should be the preferred way of accessing paths as those from untrusted
+ /// locations can be
+ ///
+ /// As paths perform no conversions, this will never fail.
+ pub fn path_filter(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ filter: &mut MetadataFilter,
+ ) -> Option<crate::Path<'_>> {
+ self.raw_value_filter(section_name, subsection_name, key, filter)
+ .ok()
+ .map(crate::Path::from)
+ }
+
+ /// Like [`path_filter()`][File::path_filter()], but suitable for statically known `key`s like `remote.origin.url`.
+ pub fn path_filter_by_key<'a>(
+ &self,
+ key: impl Into<&'a BStr>,
+ filter: &mut MetadataFilter,
+ ) -> Option<crate::Path<'_>> {
+ let key = crate::parse::key(key)?;
+ self.path_filter(key.section_name, key.subsection_name, key.value_name, filter)
+ }
+
+ /// Like [`value()`][File::value()], but returning `None` if the boolean value wasn't found.
+ pub fn boolean(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ ) -> Option<Result<bool, value::Error>> {
+ self.boolean_filter(section_name, subsection_name, key, &mut |_| true)
+ }
+
+ /// Like [`boolean()`][File::boolean()], but suitable for statically known `key`s like `remote.origin.url`.
+ pub fn boolean_by_key<'a>(&self, key: impl Into<&'a BStr>) -> Option<Result<bool, value::Error>> {
+ self.boolean_filter_by_key(key, &mut |_| true)
+ }
+
+ /// Like [`boolean()`][File::boolean()], but the section containing the returned value must pass `filter` as well.
+ pub fn boolean_filter(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ filter: &mut MetadataFilter,
+ ) -> Option<Result<bool, value::Error>> {
+ let section_name = section_name.as_ref();
+ let section_ids = self
+ .section_ids_by_name_and_subname(section_name, subsection_name)
+ .ok()?;
+ let key = key.as_ref();
+ for section_id in section_ids.rev() {
+ let section = self.sections.get(&section_id).expect("known section id");
+ if !filter(section.meta()) {
+ continue;
+ }
+ match section.value_implicit(key) {
+ Some(Some(v)) => return Some(crate::Boolean::try_from(v).map(|b| b.into())),
+ Some(None) => return Some(Ok(true)),
+ None => continue,
+ }
+ }
+ None
+ }
+
+ /// Like [`boolean_filter()`][File::boolean_filter()], but suitable for statically known `key`s like `remote.origin.url`.
+ pub fn boolean_filter_by_key<'a>(
+ &self,
+ key: impl Into<&'a BStr>,
+ filter: &mut MetadataFilter,
+ ) -> Option<Result<bool, value::Error>> {
+ let key = crate::parse::key(key)?;
+ self.boolean_filter(key.section_name, key.subsection_name, key.value_name, filter)
+ }
+
+ /// Like [`value()`][File::value()], but returning an `Option` if the integer wasn't found.
+ pub fn integer(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ ) -> Option<Result<i64, value::Error>> {
+ self.integer_filter(section_name, subsection_name, key, &mut |_| true)
+ }
+
+ /// Like [`integer()`][File::integer()], but suitable for statically known `key`s like `remote.origin.url`.
+ pub fn integer_by_key<'a>(&self, key: impl Into<&'a BStr>) -> Option<Result<i64, value::Error>> {
+ self.integer_filter_by_key(key, &mut |_| true)
+ }
+
+ /// Like [`integer()`][File::integer()], but the section containing the returned value must pass `filter` as well.
+ pub fn integer_filter(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ filter: &mut MetadataFilter,
+ ) -> Option<Result<i64, value::Error>> {
+ let int = self.raw_value_filter(section_name, subsection_name, key, filter).ok()?;
+ Some(crate::Integer::try_from(int.as_ref()).and_then(|b| {
+ b.to_decimal()
+ .ok_or_else(|| value::Error::new("Integer overflow", int.into_owned()))
+ }))
+ }
+
+ /// Like [`integer_filter()`][File::integer_filter()], but suitable for statically known `key`s like `remote.origin.url`.
+ pub fn integer_filter_by_key<'a>(
+ &self,
+ key: impl Into<&'a BStr>,
+ filter: &mut MetadataFilter,
+ ) -> Option<Result<i64, value::Error>> {
+ let key = crate::parse::key(key)?;
+ self.integer_filter(key.section_name, key.subsection_name, key.value_name, filter)
+ }
+
+ /// Similar to [`values(…)`][File::values()] but returning strings if at least one of them was found.
+ pub fn strings(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ ) -> Option<Vec<Cow<'_, BStr>>> {
+ self.raw_values(section_name, subsection_name, key).ok()
+ }
+
+ /// Like [`strings()`][File::strings()], but suitable for statically known `key`s like `remote.origin.url`.
+ pub fn strings_by_key<'a>(&self, key: impl Into<&'a BStr>) -> Option<Vec<Cow<'_, BStr>>> {
+ let key = crate::parse::key(key)?;
+ self.strings(key.section_name, key.subsection_name, key.value_name)
+ }
+
+ /// Similar to [`strings(…)`][File::strings()], but all values are in sections that passed `filter`.
+ pub fn strings_filter(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ filter: &mut MetadataFilter,
+ ) -> Option<Vec<Cow<'_, BStr>>> {
+ self.raw_values_filter(section_name, subsection_name, key, filter).ok()
+ }
+
+ /// Like [`strings_filter()`][File::strings_filter()], but suitable for statically known `key`s like `remote.origin.url`.
+ pub fn strings_filter_by_key<'a>(
+ &self,
+ key: impl Into<&'a BStr>,
+ filter: &mut MetadataFilter,
+ ) -> Option<Vec<Cow<'_, BStr>>> {
+ let key = crate::parse::key(key)?;
+ self.strings_filter(key.section_name, key.subsection_name, key.value_name, filter)
+ }
+
+ /// Similar to [`values(…)`][File::values()] but returning integers if at least one of them was found
+ /// and if none of them overflows.
+ pub fn integers(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ ) -> Option<Result<Vec<i64>, value::Error>> {
+ self.integers_filter(section_name, subsection_name, key, &mut |_| true)
+ }
+
+ /// Like [`integers()`][File::integers()], but suitable for statically known `key`s like `remote.origin.url`.
+ pub fn integers_by_key<'a>(&self, key: impl Into<&'a BStr>) -> Option<Result<Vec<i64>, value::Error>> {
+ self.integers_filter_by_key(key, &mut |_| true)
+ }
+
+ /// Similar to [`integers(…)`][File::integers()] but all integers are in sections that passed `filter`
+ /// and that are not overflowing.
+ pub fn integers_filter(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ filter: &mut MetadataFilter,
+ ) -> Option<Result<Vec<i64>, value::Error>> {
+ self.raw_values_filter(section_name, subsection_name, key, filter)
+ .ok()
+ .map(|values| {
+ values
+ .into_iter()
+ .map(|v| {
+ crate::Integer::try_from(v.as_ref()).and_then(|int| {
+ int.to_decimal()
+ .ok_or_else(|| value::Error::new("Integer overflow", v.into_owned()))
+ })
+ })
+ .collect()
+ })
+ }
+
+ /// Like [`integers_filter()`][File::integers_filter()], but suitable for statically known `key`s like `remote.origin.url`.
+ pub fn integers_filter_by_key<'a>(
+ &self,
+ key: impl Into<&'a BStr>,
+ filter: &mut MetadataFilter,
+ ) -> Option<Result<Vec<i64>, value::Error>> {
+ let key = crate::parse::key(key)?;
+ self.integers_filter(key.section_name, key.subsection_name, key.value_name, filter)
+ }
+}
diff --git a/vendor/gix-config/src/file/access/mod.rs b/vendor/gix-config/src/file/access/mod.rs
new file mode 100644
index 000000000..d602b5f8b
--- /dev/null
+++ b/vendor/gix-config/src/file/access/mod.rs
@@ -0,0 +1,4 @@
+mod comfort;
+mod mutate;
+mod raw;
+mod read_only;
diff --git a/vendor/gix-config/src/file/access/mutate.rs b/vendor/gix-config/src/file/access/mutate.rs
new file mode 100644
index 000000000..e1cfc6e1c
--- /dev/null
+++ b/vendor/gix-config/src/file/access/mutate.rs
@@ -0,0 +1,387 @@
+use std::borrow::Cow;
+
+use bstr::BStr;
+use gix_features::threading::OwnShared;
+
+use crate::{
+ file::{self, rename_section, write::ends_with_newline, MetadataFilter, SectionBodyIdsLut, SectionId, SectionMut},
+ lookup,
+ parse::{section, Event, FrontMatterEvents},
+ File,
+};
+
+/// Mutating low-level access methods.
+impl<'event> File<'event> {
+ /// Returns the last mutable section with a given `name` and optional `subsection_name`, _if it exists_.
+ pub fn section_mut<'a>(
+ &'a mut self,
+ name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ ) -> Result<SectionMut<'a, 'event>, lookup::existing::Error> {
+ let id = self
+ .section_ids_by_name_and_subname(name.as_ref(), subsection_name)?
+ .rev()
+ .next()
+ .expect("BUG: Section lookup vec was empty");
+ let nl = self.detect_newline_style_smallvec();
+ Ok(self
+ .sections
+ .get_mut(&id)
+ .expect("BUG: Section did not have id from lookup")
+ .to_mut(nl))
+ }
+
+ /// Returns the last found mutable section with a given `key`, identifying the name and subsection name like `core` or `remote.origin`.
+ pub fn section_mut_by_key<'a, 'b>(
+ &'a mut self,
+ key: impl Into<&'b BStr>,
+ ) -> Result<SectionMut<'a, 'event>, lookup::existing::Error> {
+ let key = section::unvalidated::Key::parse(key).ok_or(lookup::existing::Error::KeyMissing)?;
+ self.section_mut(key.section_name, key.subsection_name)
+ }
+
+ /// Return the mutable section identified by `id`, or `None` if it didn't exist.
+ ///
+ /// Note that `id` is stable across deletions and insertions.
+ pub fn section_mut_by_id<'a>(&'a mut self, id: SectionId) -> Option<SectionMut<'a, 'event>> {
+ let nl = self.detect_newline_style_smallvec();
+ self.sections.get_mut(&id).map(|s| s.to_mut(nl))
+ }
+
+ /// Returns the last mutable section with a given `name` and optional `subsection_name`, _if it exists_, or create a new section.
+ pub fn section_mut_or_create_new<'a>(
+ &'a mut self,
+ name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ ) -> Result<SectionMut<'a, 'event>, section::header::Error> {
+ self.section_mut_or_create_new_filter(name, subsection_name, &mut |_| true)
+ }
+
+ /// Returns an mutable section with a given `name` and optional `subsection_name`, _if it exists_ **and** passes `filter`, or create
+ /// a new section.
+ pub fn section_mut_or_create_new_filter<'a>(
+ &'a mut self,
+ name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ filter: &mut MetadataFilter,
+ ) -> Result<SectionMut<'a, 'event>, section::header::Error> {
+ let name = name.as_ref();
+ match self
+ .section_ids_by_name_and_subname(name.as_ref(), subsection_name)
+ .ok()
+ .and_then(|it| {
+ it.rev().find(|id| {
+ let s = &self.sections[id];
+ filter(s.meta())
+ })
+ }) {
+ Some(id) => {
+ let nl = self.detect_newline_style_smallvec();
+ Ok(self
+ .sections
+ .get_mut(&id)
+ .expect("BUG: Section did not have id from lookup")
+ .to_mut(nl))
+ }
+ None => self.new_section(name.to_owned(), subsection_name.map(|n| Cow::Owned(n.to_owned()))),
+ }
+ }
+
+ /// Returns the last found mutable section with a given `name` and optional `subsection_name`, that matches `filter`, _if it exists_.
+ ///
+ /// If there are sections matching `section_name` and `subsection_name` but the `filter` rejects all of them, `Ok(None)`
+ /// is returned.
+ pub fn section_mut_filter<'a>(
+ &'a mut self,
+ name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ filter: &mut MetadataFilter,
+ ) -> Result<Option<file::SectionMut<'a, 'event>>, lookup::existing::Error> {
+ let id = self
+ .section_ids_by_name_and_subname(name.as_ref(), subsection_name)?
+ .rev()
+ .find(|id| {
+ let s = &self.sections[id];
+ filter(s.meta())
+ });
+ let nl = self.detect_newline_style_smallvec();
+ Ok(id.and_then(move |id| self.sections.get_mut(&id).map(move |s| s.to_mut(nl))))
+ }
+
+ /// Like [`section_mut_filter()`][File::section_mut_filter()], but identifies the with a given `key`,
+ /// like `core` or `remote.origin`.
+ pub fn section_mut_filter_by_key<'a, 'b>(
+ &'a mut self,
+ key: impl Into<&'b BStr>,
+ filter: &mut MetadataFilter,
+ ) -> Result<Option<file::SectionMut<'a, 'event>>, lookup::existing::Error> {
+ let key = section::unvalidated::Key::parse(key).ok_or(lookup::existing::Error::KeyMissing)?;
+ self.section_mut_filter(key.section_name, key.subsection_name, filter)
+ }
+
+ /// Adds a new section. If a subsection name was provided, then
+ /// the generated header will use the modern subsection syntax.
+ /// Returns a reference to the new section for immediate editing.
+ ///
+ /// # Examples
+ ///
+ /// Creating a new empty section:
+ ///
+ /// ```
+ /// # use std::borrow::Cow;
+ /// # use gix_config::File;
+ /// # use std::convert::TryFrom;
+ /// let mut gix_config = gix_config::File::default();
+ /// let section = gix_config.new_section("hello", Some(Cow::Borrowed("world".into())))?;
+ /// let nl = section.newline().to_owned();
+ /// assert_eq!(gix_config.to_string(), format!("[hello \"world\"]{nl}"));
+ /// # Ok::<(), Box<dyn std::error::Error>>(())
+ /// ```
+ ///
+ /// Creating a new empty section and adding values to it:
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use std::borrow::Cow;
+ /// # use std::convert::TryFrom;
+ /// # use bstr::ByteSlice;
+ /// # use gix_config::parse::section;
+ /// let mut gix_config = gix_config::File::default();
+ /// let mut section = gix_config.new_section("hello", Some(Cow::Borrowed("world".into())))?;
+ /// section.push(section::Key::try_from("a")?, Some("b".into()));
+ /// let nl = section.newline().to_owned();
+ /// assert_eq!(gix_config.to_string(), format!("[hello \"world\"]{nl}\ta = b{nl}"));
+ /// let _section = gix_config.new_section("core", None);
+ /// assert_eq!(gix_config.to_string(), format!("[hello \"world\"]{nl}\ta = b{nl}[core]{nl}"));
+ /// # Ok::<(), Box<dyn std::error::Error>>(())
+ /// ```
+ pub fn new_section(
+ &mut self,
+ name: impl Into<Cow<'event, str>>,
+ subsection: impl Into<Option<Cow<'event, BStr>>>,
+ ) -> Result<SectionMut<'_, 'event>, section::header::Error> {
+ let id = self.push_section_internal(file::Section::new(name, subsection, OwnShared::clone(&self.meta))?);
+ let nl = self.detect_newline_style_smallvec();
+ let mut section = self.sections.get_mut(&id).expect("each id yields a section").to_mut(nl);
+ section.push_newline();
+ Ok(section)
+ }
+
+ /// Removes the section with `name` and `subsection_name` , returning it if there was a matching section.
+ /// If multiple sections have the same name, then the last one is returned. Note that
+ /// later sections with the same name have precedent over earlier ones.
+ ///
+ /// # Examples
+ ///
+ /// Creating and removing a section:
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use std::convert::TryFrom;
+ /// let mut gix_config = gix_config::File::try_from(
+ /// r#"[hello "world"]
+ /// some-value = 4
+ /// "#)?;
+ ///
+ /// let section = gix_config.remove_section("hello", Some("world".into()));
+ /// assert_eq!(gix_config.to_string(), "");
+ /// # Ok::<(), Box<dyn std::error::Error>>(())
+ /// ```
+ ///
+ /// Precedence example for removing sections with the same name:
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use std::convert::TryFrom;
+ /// let mut gix_config = gix_config::File::try_from(
+ /// r#"[hello "world"]
+ /// some-value = 4
+ /// [hello "world"]
+ /// some-value = 5
+ /// "#)?;
+ ///
+ /// let section = gix_config.remove_section("hello", Some("world".into()));
+ /// assert_eq!(gix_config.to_string(), "[hello \"world\"]\n some-value = 4\n");
+ /// # Ok::<(), Box<dyn std::error::Error>>(())
+ /// ```
+ pub fn remove_section<'a>(
+ &mut self,
+ name: &str,
+ subsection_name: impl Into<Option<&'a BStr>>,
+ ) -> Option<file::Section<'event>> {
+ let id = self
+ .section_ids_by_name_and_subname(name, subsection_name.into())
+ .ok()?
+ .rev()
+ .next()?;
+ self.remove_section_by_id(id)
+ }
+
+ /// Remove the section identified by `id` if it exists and return it, or return `None` if no such section was present.
+ ///
+ /// Note that section ids are unambiguous even in the face of removals and additions of sections.
+ pub fn remove_section_by_id(&mut self, id: SectionId) -> Option<file::Section<'event>> {
+ self.section_order
+ .remove(self.section_order.iter().position(|v| *v == id)?);
+ let section = self.sections.remove(&id)?;
+ let lut = self
+ .section_lookup_tree
+ .get_mut(&section.header.name)
+ .expect("lookup cache still has name to be deleted");
+ // NOTE: this leaves empty lists in the data structure which our code now has to deal with.
+ for entry in lut {
+ match section.header.subsection_name.as_deref() {
+ Some(subsection_name) => {
+ if let SectionBodyIdsLut::NonTerminal(map) = entry {
+ if let Some(ids) = map.get_mut(subsection_name) {
+ ids.remove(ids.iter().position(|v| *v == id).expect("present"));
+ break;
+ }
+ }
+ }
+ None => {
+ if let SectionBodyIdsLut::Terminal(ids) = entry {
+ ids.remove(ids.iter().position(|v| *v == id).expect("present"));
+ break;
+ }
+ }
+ }
+ }
+ Some(section)
+ }
+
+ /// Removes the section with `name` and `subsection_name` that passed `filter`, returning the removed section
+ /// if at least one section matched the `filter`.
+ /// If multiple sections have the same name, then the last one is returned. Note that
+ /// later sections with the same name have precedent over earlier ones.
+ pub fn remove_section_filter<'a>(
+ &mut self,
+ name: &str,
+ subsection_name: impl Into<Option<&'a BStr>>,
+ filter: &mut MetadataFilter,
+ ) -> Option<file::Section<'event>> {
+ let id = self
+ .section_ids_by_name_and_subname(name, subsection_name.into())
+ .ok()?
+ .rev()
+ .find(|id| filter(self.sections.get(id).expect("each id has a section").meta()))?;
+ self.section_order.remove(
+ self.section_order
+ .iter()
+ .position(|v| *v == id)
+ .expect("known section id"),
+ );
+ self.sections.remove(&id)
+ }
+
+ /// Adds the provided section to the config, returning a mutable reference
+ /// to it for immediate editing.
+ /// Note that its meta-data will remain as is.
+ pub fn push_section(
+ &mut self,
+ section: file::Section<'event>,
+ ) -> Result<SectionMut<'_, 'event>, section::header::Error> {
+ let id = self.push_section_internal(section);
+ let nl = self.detect_newline_style_smallvec();
+ let section = self.sections.get_mut(&id).expect("each id yields a section").to_mut(nl);
+ Ok(section)
+ }
+
+ /// Renames the section with `name` and `subsection_name`, modifying the last matching section
+ /// to use `new_name` and `new_subsection_name`.
+ pub fn rename_section<'a>(
+ &mut self,
+ name: impl AsRef<str>,
+ subsection_name: impl Into<Option<&'a BStr>>,
+ new_name: impl Into<Cow<'event, str>>,
+ new_subsection_name: impl Into<Option<Cow<'event, BStr>>>,
+ ) -> Result<(), rename_section::Error> {
+ let id = self
+ .section_ids_by_name_and_subname(name.as_ref(), subsection_name.into())?
+ .rev()
+ .next()
+ .expect("list of sections were empty, which violates invariant");
+ let section = self.sections.get_mut(&id).expect("known section-id");
+ section.header = section::Header::new(new_name, new_subsection_name)?;
+ Ok(())
+ }
+
+ /// Renames the section with `name` and `subsection_name`, modifying the last matching section
+ /// that also passes `filter` to use `new_name` and `new_subsection_name`.
+ ///
+ /// Note that the otherwise unused [`lookup::existing::Error::KeyMissing`] variant is used to indicate
+ /// that the `filter` rejected all candidates, leading to no section being renamed after all.
+ pub fn rename_section_filter<'a>(
+ &mut self,
+ name: impl AsRef<str>,
+ subsection_name: impl Into<Option<&'a BStr>>,
+ new_name: impl Into<Cow<'event, str>>,
+ new_subsection_name: impl Into<Option<Cow<'event, BStr>>>,
+ filter: &mut MetadataFilter,
+ ) -> Result<(), rename_section::Error> {
+ let id = self
+ .section_ids_by_name_and_subname(name.as_ref(), subsection_name.into())?
+ .rev()
+ .find(|id| filter(self.sections.get(id).expect("each id has a section").meta()))
+ .ok_or(rename_section::Error::Lookup(lookup::existing::Error::KeyMissing))?;
+ let section = self.sections.get_mut(&id).expect("known section-id");
+ section.header = section::Header::new(new_name, new_subsection_name)?;
+ Ok(())
+ }
+
+ /// Append another File to the end of ourselves, without losing any information.
+ pub fn append(&mut self, other: Self) -> &mut Self {
+ self.append_or_insert(other, None)
+ }
+
+ /// Append another File to the end of ourselves, without losing any information.
+ pub(crate) fn append_or_insert(&mut self, mut other: Self, mut insert_after: Option<SectionId>) -> &mut Self {
+ let nl = self.detect_newline_style_smallvec();
+ fn extend_and_assure_newline<'a>(
+ lhs: &mut FrontMatterEvents<'a>,
+ rhs: FrontMatterEvents<'a>,
+ nl: &impl AsRef<[u8]>,
+ ) {
+ if !ends_with_newline(lhs.as_ref(), nl, true)
+ && !rhs.first().map_or(true, |e| e.to_bstr_lossy().starts_with(nl.as_ref()))
+ {
+ lhs.push(Event::Newline(Cow::Owned(nl.as_ref().into())))
+ }
+ lhs.extend(rhs);
+ }
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ let our_last_section_before_append =
+ insert_after.or_else(|| (self.section_id_counter != 0).then(|| SectionId(self.section_id_counter - 1)));
+
+ for id in std::mem::take(&mut other.section_order) {
+ let section = other.sections.remove(&id).expect("present");
+
+ let new_id = match insert_after {
+ Some(id) => {
+ let new_id = self.insert_section_after(section, id);
+ insert_after = Some(new_id);
+ new_id
+ }
+ None => self.push_section_internal(section),
+ };
+
+ if let Some(post_matter) = other.frontmatter_post_section.remove(&id) {
+ self.frontmatter_post_section.insert(new_id, post_matter);
+ }
+ }
+
+ if other.frontmatter_events.is_empty() {
+ return self;
+ }
+
+ match our_last_section_before_append {
+ Some(last_id) => extend_and_assure_newline(
+ self.frontmatter_post_section.entry(last_id).or_default(),
+ other.frontmatter_events,
+ &nl,
+ ),
+ None => extend_and_assure_newline(&mut self.frontmatter_events, other.frontmatter_events, &nl),
+ }
+ self
+ }
+}
diff --git a/vendor/gix-config/src/file/access/raw.rs b/vendor/gix-config/src/file/access/raw.rs
new file mode 100644
index 000000000..46f1fb006
--- /dev/null
+++ b/vendor/gix-config/src/file/access/raw.rs
@@ -0,0 +1,536 @@
+use std::{borrow::Cow, collections::HashMap, convert::TryInto};
+
+use bstr::BStr;
+use smallvec::ToSmallVec;
+
+use crate::{
+ file::{mutable::multi_value::EntryData, Index, MetadataFilter, MultiValueMut, Size, ValueMut},
+ lookup,
+ parse::{section, Event},
+ File,
+};
+
+/// # Raw value API
+///
+/// These functions are the raw value API, returning normalized byte strings.
+impl<'event> File<'event> {
+ /// Returns an uninterpreted value given a section, an optional subsection
+ /// and key.
+ ///
+ /// Consider [`Self::raw_values()`] if you want to get all values of
+ /// a multivar instead.
+ pub fn raw_value(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ ) -> Result<Cow<'_, BStr>, lookup::existing::Error> {
+ self.raw_value_filter(section_name, subsection_name, key, &mut |_| true)
+ }
+
+ /// Returns an uninterpreted value given a section, an optional subsection
+ /// and key, if it passes the `filter`.
+ ///
+ /// Consider [`Self::raw_values()`] if you want to get all values of
+ /// a multivar instead.
+ pub fn raw_value_filter(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ filter: &mut MetadataFilter,
+ ) -> Result<Cow<'_, BStr>, lookup::existing::Error> {
+ let section_ids = self.section_ids_by_name_and_subname(section_name.as_ref(), subsection_name)?;
+ let key = key.as_ref();
+ for section_id in section_ids.rev() {
+ let section = self.sections.get(&section_id).expect("known section id");
+ if !filter(section.meta()) {
+ continue;
+ }
+ if let Some(v) = section.value(key) {
+ return Ok(v);
+ }
+ }
+
+ Err(lookup::existing::Error::KeyMissing)
+ }
+
+ /// Returns a mutable reference to an uninterpreted value given a section,
+ /// an optional subsection and key.
+ ///
+ /// Consider [`Self::raw_values_mut`] if you want to get mutable
+ /// references to all values of a multivar instead.
+ pub fn raw_value_mut<'lookup>(
+ &mut self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&'lookup BStr>,
+ key: &'lookup str,
+ ) -> Result<ValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
+ self.raw_value_mut_filter(section_name, subsection_name, key, &mut |_| true)
+ }
+
+ /// Returns a mutable reference to an uninterpreted value given a section,
+ /// an optional subsection and key, and if it passes `filter`.
+ ///
+ /// Consider [`Self::raw_values_mut`] if you want to get mutable
+ /// references to all values of a multivar instead.
+ pub fn raw_value_mut_filter<'lookup>(
+ &mut self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&'lookup BStr>,
+ key: &'lookup str,
+ filter: &mut MetadataFilter,
+ ) -> Result<ValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
+ let mut section_ids = self
+ .section_ids_by_name_and_subname(section_name.as_ref(), subsection_name)?
+ .rev();
+ let key = section::Key(Cow::<BStr>::Borrowed(key.into()));
+
+ while let Some(section_id) = section_ids.next() {
+ let mut index = 0;
+ let mut size = 0;
+ let mut found_key = false;
+ let section = self.sections.get(&section_id).expect("known section id");
+ if !filter(section.meta()) {
+ continue;
+ }
+ for (i, event) in section.as_ref().iter().enumerate() {
+ match event {
+ Event::SectionKey(event_key) if *event_key == key => {
+ found_key = true;
+ index = i;
+ size = 1;
+ }
+ Event::Newline(_) | Event::Whitespace(_) | Event::ValueNotDone(_) if found_key => {
+ size += 1;
+ }
+ Event::ValueDone(_) | Event::Value(_) if found_key => {
+ found_key = false;
+ size += 1;
+ }
+ Event::KeyValueSeparator if found_key => {
+ size += 1;
+ }
+ _ => {}
+ }
+ }
+
+ if size == 0 {
+ continue;
+ }
+
+ drop(section_ids);
+ let nl = self.detect_newline_style().to_smallvec();
+ return Ok(ValueMut {
+ section: self.sections.get_mut(&section_id).expect("known section-id").to_mut(nl),
+ key,
+ index: Index(index),
+ size: Size(size),
+ });
+ }
+
+ Err(lookup::existing::Error::KeyMissing)
+ }
+
+ /// Returns all uninterpreted values given a section, an optional subsection
+ /// ain order of occurrence.
+ ///
+ /// The ordering means that the last of the returned values is the one that would be the
+ /// value used in the single-value case.nd key.
+ ///
+ /// # Examples
+ ///
+ /// If you have the following config:
+ ///
+ /// ```text
+ /// [core]
+ /// a = b
+ /// [core]
+ /// a = c
+ /// a = d
+ /// ```
+ ///
+ /// Attempting to get all values of `a` yields the following:
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use std::borrow::Cow;
+ /// # use std::convert::TryFrom;
+ /// # use bstr::BStr;
+ /// # let gix_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
+ /// assert_eq!(
+ /// gix_config.raw_values("core", None, "a").unwrap(),
+ /// vec![
+ /// Cow::<BStr>::Borrowed("b".into()),
+ /// Cow::<BStr>::Borrowed("c".into()),
+ /// Cow::<BStr>::Borrowed("d".into()),
+ /// ],
+ /// );
+ /// ```
+ ///
+ /// Consider [`Self::raw_value`] if you want to get the resolved single
+ /// value for a given key, if your key does not support multi-valued values.
+ pub fn raw_values(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ ) -> Result<Vec<Cow<'_, BStr>>, lookup::existing::Error> {
+ self.raw_values_filter(section_name, subsection_name, key, &mut |_| true)
+ }
+
+ /// Returns all uninterpreted values given a section, an optional subsection
+ /// and key, if the value passes `filter`, in order of occurrence.
+ ///
+ /// The ordering means that the last of the returned values is the one that would be the
+ /// value used in the single-value case.
+ pub fn raw_values_filter(
+ &self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ filter: &mut MetadataFilter,
+ ) -> Result<Vec<Cow<'_, BStr>>, lookup::existing::Error> {
+ let mut values = Vec::new();
+ let section_ids = self.section_ids_by_name_and_subname(section_name.as_ref(), subsection_name)?;
+ let key = key.as_ref();
+ for section_id in section_ids {
+ let section = self.sections.get(&section_id).expect("known section id");
+ if !filter(section.meta()) {
+ continue;
+ }
+ values.extend(section.values(key));
+ }
+
+ if values.is_empty() {
+ Err(lookup::existing::Error::KeyMissing)
+ } else {
+ Ok(values)
+ }
+ }
+
+ /// Returns mutable references to all uninterpreted values given a section,
+ /// an optional subsection and key.
+ ///
+ /// # Examples
+ ///
+ /// If you have the following config:
+ ///
+ /// ```text
+ /// [core]
+ /// a = b
+ /// [core]
+ /// a = c
+ /// a = d
+ /// ```
+ ///
+ /// Attempting to get all values of `a` yields the following:
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use std::borrow::Cow;
+ /// # use std::convert::TryFrom;
+ /// # use bstr::BStr;
+ /// # let mut gix_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
+ /// assert_eq!(
+ /// gix_config.raw_values("core", None, "a")?,
+ /// vec![
+ /// Cow::<BStr>::Borrowed("b".into()),
+ /// Cow::<BStr>::Borrowed("c".into()),
+ /// Cow::<BStr>::Borrowed("d".into())
+ /// ]
+ /// );
+ ///
+ /// gix_config.raw_values_mut("core", None, "a")?.set_all("g");
+ ///
+ /// assert_eq!(
+ /// gix_config.raw_values("core", None, "a")?,
+ /// vec![
+ /// Cow::<BStr>::Borrowed("g".into()),
+ /// Cow::<BStr>::Borrowed("g".into()),
+ /// Cow::<BStr>::Borrowed("g".into())
+ /// ],
+ /// );
+ /// # Ok::<(), gix_config::lookup::existing::Error>(())
+ /// ```
+ ///
+ /// Consider [`Self::raw_value`] if you want to get the resolved single
+ /// value for a given key, if your key does not support multi-valued values.
+ ///
+ /// Note that this operation is relatively expensive, requiring a full
+ /// traversal of the config.
+ pub fn raw_values_mut<'lookup>(
+ &mut self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&'lookup BStr>,
+ key: &'lookup str,
+ ) -> Result<MultiValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
+ self.raw_values_mut_filter(section_name, subsection_name, key, &mut |_| true)
+ }
+
+ /// Returns mutable references to all uninterpreted values given a section,
+ /// an optional subsection and key, if their sections pass `filter`.
+ pub fn raw_values_mut_filter<'lookup>(
+ &mut self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&'lookup BStr>,
+ key: &'lookup str,
+ filter: &mut MetadataFilter,
+ ) -> Result<MultiValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
+ let section_ids = self.section_ids_by_name_and_subname(section_name.as_ref(), subsection_name)?;
+ let key = section::Key(Cow::<BStr>::Borrowed(key.into()));
+
+ let mut offsets = HashMap::new();
+ let mut entries = Vec::new();
+ for section_id in section_ids.rev() {
+ let mut last_boundary = 0;
+ let mut expect_value = false;
+ let mut offset_list = Vec::new();
+ let mut offset_index = 0;
+ let section = self.sections.get(&section_id).expect("known section-id");
+ if !filter(section.meta()) {
+ continue;
+ }
+ for (i, event) in section.as_ref().iter().enumerate() {
+ match event {
+ Event::SectionKey(event_key) if *event_key == key => {
+ expect_value = true;
+ offset_list.push(i - last_boundary);
+ offset_index += 1;
+ last_boundary = i;
+ }
+ Event::Value(_) | Event::ValueDone(_) if expect_value => {
+ expect_value = false;
+ entries.push(EntryData {
+ section_id,
+ offset_index,
+ });
+ offset_list.push(i - last_boundary + 1);
+ offset_index += 1;
+ last_boundary = i + 1;
+ }
+ _ => (),
+ }
+ }
+ offsets.insert(section_id, offset_list);
+ }
+
+ entries.sort();
+
+ if entries.is_empty() {
+ Err(lookup::existing::Error::KeyMissing)
+ } else {
+ Ok(MultiValueMut {
+ section: &mut self.sections,
+ key,
+ indices_and_sizes: entries,
+ offsets,
+ })
+ }
+ }
+
+ /// Sets a value in a given `section_name`, optional `subsection_name`, and `key`.
+ /// Note sections named `section_name` and `subsection_name` (if not `None`)
+ /// must exist for this method to work.
+ ///
+ /// # Examples
+ ///
+ /// Given the config,
+ ///
+ /// ```text
+ /// [core]
+ /// a = b
+ /// [core]
+ /// a = c
+ /// a = d
+ /// ```
+ ///
+ /// Setting a new value to the key `core.a` will yield the following:
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use std::borrow::Cow;
+ /// # use bstr::BStr;
+ /// # use std::convert::TryFrom;
+ /// # let mut gix_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
+ /// gix_config.set_existing_raw_value("core", None, "a", "e")?;
+ /// assert_eq!(gix_config.raw_value("core", None, "a")?, Cow::<BStr>::Borrowed("e".into()));
+ /// assert_eq!(
+ /// gix_config.raw_values("core", None, "a")?,
+ /// vec![
+ /// Cow::<BStr>::Borrowed("b".into()),
+ /// Cow::<BStr>::Borrowed("c".into()),
+ /// Cow::<BStr>::Borrowed("e".into())
+ /// ],
+ /// );
+ /// # Ok::<(), Box<dyn std::error::Error>>(())
+ /// ```
+ pub fn set_existing_raw_value<'b>(
+ &mut self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ new_value: impl Into<&'b BStr>,
+ ) -> Result<(), lookup::existing::Error> {
+ self.raw_value_mut(section_name, subsection_name, key.as_ref())
+ .map(|mut entry| entry.set(new_value))
+ }
+
+ /// Sets a value in a given `section_name`, optional `subsection_name`, and `key`.
+ /// Creates the section if necessary and the key as well, or overwrites the last existing value otherwise.
+ ///
+ /// # Examples
+ ///
+ /// Given the config,
+ ///
+ /// ```text
+ /// [core]
+ /// a = b
+ /// ```
+ ///
+ /// Setting a new value to the key `core.a` will yield the following:
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use std::borrow::Cow;
+ /// # use bstr::BStr;
+ /// # use std::convert::TryFrom;
+ /// # let mut gix_config = gix_config::File::try_from("[core]a=b").unwrap();
+ /// let prev = gix_config.set_raw_value("core", None, "a", "e")?;
+ /// gix_config.set_raw_value("core", None, "b", "f")?;
+ /// assert_eq!(prev.expect("present").as_ref(), "b");
+ /// assert_eq!(gix_config.raw_value("core", None, "a")?, Cow::<BStr>::Borrowed("e".into()));
+ /// assert_eq!(gix_config.raw_value("core", None, "b")?, Cow::<BStr>::Borrowed("f".into()));
+ /// # Ok::<(), Box<dyn std::error::Error>>(())
+ /// ```
+ pub fn set_raw_value<'b, Key, E>(
+ &mut self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: Key,
+ new_value: impl Into<&'b BStr>,
+ ) -> Result<Option<Cow<'event, BStr>>, crate::file::set_raw_value::Error>
+ where
+ Key: TryInto<section::Key<'event>, Error = E>,
+ section::key::Error: From<E>,
+ {
+ self.set_raw_value_filter(section_name, subsection_name, key, new_value, &mut |_| true)
+ }
+
+ /// Similar to [`set_raw_value()`][Self::set_raw_value()], but only sets existing values in sections matching
+ /// `filter`, creating a new section otherwise.
+ pub fn set_raw_value_filter<'b, Key, E>(
+ &mut self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: Key,
+ new_value: impl Into<&'b BStr>,
+ filter: &mut MetadataFilter,
+ ) -> Result<Option<Cow<'event, BStr>>, crate::file::set_raw_value::Error>
+ where
+ Key: TryInto<section::Key<'event>, Error = E>,
+ section::key::Error: From<E>,
+ {
+ let mut section = self.section_mut_or_create_new_filter(section_name, subsection_name, filter)?;
+ Ok(section.set(key.try_into().map_err(section::key::Error::from)?, new_value))
+ }
+
+ /// Sets a multivar in a given section, optional subsection, and key value.
+ ///
+ /// This internally zips together the new values and the existing values.
+ /// As a result, if more new values are provided than the current amount of
+ /// multivars, then the latter values are not applied. If there are less
+ /// new values than old ones then the remaining old values are unmodified.
+ ///
+ /// **Note**: Mutation order is _not_ guaranteed and is non-deterministic.
+ /// If you need finer control over which values of the multivar are set,
+ /// consider using [`raw_values_mut()`][Self::raw_values_mut()], which will let you iterate
+ /// and check over the values instead. This is best used as a convenience
+ /// function for setting multivars whose values should be treated as an
+ /// unordered set.
+ ///
+ /// # Examples
+ ///
+ /// Let us use the follow config for all examples:
+ ///
+ /// ```text
+ /// [core]
+ /// a = b
+ /// [core]
+ /// a = c
+ /// a = d
+ /// ```
+ ///
+ /// Setting an equal number of values:
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use std::borrow::Cow;
+ /// # use std::convert::TryFrom;
+ /// # use bstr::BStr;
+ /// # let mut gix_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
+ /// let new_values = vec![
+ /// "x",
+ /// "y",
+ /// "z",
+ /// ];
+ /// gix_config.set_existing_raw_multi_value("core", None, "a", new_values.into_iter())?;
+ /// let fetched_config = gix_config.raw_values("core", None, "a")?;
+ /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("x".into())));
+ /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("y".into())));
+ /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("z".into())));
+ /// # Ok::<(), gix_config::lookup::existing::Error>(())
+ /// ```
+ ///
+ /// Setting less than the number of present values sets the first ones found:
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use std::borrow::Cow;
+ /// # use std::convert::TryFrom;
+ /// # use bstr::BStr;
+ /// # let mut gix_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
+ /// let new_values = vec![
+ /// "x",
+ /// "y",
+ /// ];
+ /// gix_config.set_existing_raw_multi_value("core", None, "a", new_values.into_iter())?;
+ /// let fetched_config = gix_config.raw_values("core", None, "a")?;
+ /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("x".into())));
+ /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("y".into())));
+ /// # Ok::<(), gix_config::lookup::existing::Error>(())
+ /// ```
+ ///
+ /// Setting more than the number of present values discards the rest:
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use std::borrow::Cow;
+ /// # use std::convert::TryFrom;
+ /// # use bstr::BStr;
+ /// # let mut gix_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
+ /// let new_values = vec![
+ /// "x",
+ /// "y",
+ /// "z",
+ /// "discarded",
+ /// ];
+ /// gix_config.set_existing_raw_multi_value("core", None, "a", new_values)?;
+ /// assert!(!gix_config.raw_values("core", None, "a")?.contains(&Cow::<BStr>::Borrowed("discarded".into())));
+ /// # Ok::<(), gix_config::lookup::existing::Error>(())
+ /// ```
+ pub fn set_existing_raw_multi_value<'a, Iter, Item>(
+ &mut self,
+ section_name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ key: impl AsRef<str>,
+ new_values: Iter,
+ ) -> Result<(), lookup::existing::Error>
+ where
+ Iter: IntoIterator<Item = Item>,
+ Item: Into<&'a BStr>,
+ {
+ self.raw_values_mut(section_name, subsection_name, key.as_ref())
+ .map(|mut v| v.set_values(new_values))
+ }
+}
diff --git a/vendor/gix-config/src/file/access/read_only.rs b/vendor/gix-config/src/file/access/read_only.rs
new file mode 100644
index 000000000..5520c6566
--- /dev/null
+++ b/vendor/gix-config/src/file/access/read_only.rs
@@ -0,0 +1,353 @@
+use std::{borrow::Cow, convert::TryFrom};
+
+use bstr::{BStr, ByteSlice};
+use gix_features::threading::OwnShared;
+use smallvec::SmallVec;
+
+use crate::{
+ file,
+ file::{
+ write::{extract_newline, platform_newline},
+ Metadata, MetadataFilter, SectionId,
+ },
+ lookup,
+ parse::Event,
+ File,
+};
+
+/// Read-only low-level access methods, as it requires generics for converting into
+/// custom values defined in this crate like [`Integer`][crate::Integer] and
+/// [`Color`][crate::Color].
+impl<'event> File<'event> {
+ /// Returns an interpreted value given a section, an optional subsection and
+ /// key.
+ ///
+ /// It's recommended to use one of the value types provide dby this crate
+ /// as they implement the conversion, but this function is flexible and
+ /// will accept any type that implements [`TryFrom<&BStr>`][std::convert::TryFrom].
+ ///
+ /// Consider [`Self::values`] if you want to get all values of a multivar instead.
+ ///
+ /// If a `string` is desired, use the [`string()`][Self::string()] method instead.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use gix_config::{Integer, Boolean};
+ /// # use std::borrow::Cow;
+ /// # use std::convert::TryFrom;
+ /// let config = r#"
+ /// [core]
+ /// a = 10k
+ /// c = false
+ /// "#;
+ /// let gix_config = gix_config::File::try_from(config)?;
+ /// // You can either use the turbofish to determine the type...
+ /// let a_value = gix_config.value::<Integer>("core", None, "a")?;
+ /// // ... or explicitly declare the type to avoid the turbofish
+ /// let c_value: Boolean = gix_config.value("core", None, "c")?;
+ /// # Ok::<(), Box<dyn std::error::Error>>(())
+ /// ```
+ pub fn value<'a, T: TryFrom<Cow<'a, BStr>>>(
+ &'a self,
+ section_name: &str,
+ subsection_name: Option<&BStr>,
+ key: &str,
+ ) -> Result<T, lookup::Error<T::Error>> {
+ T::try_from(self.raw_value(section_name, subsection_name, key)?).map_err(lookup::Error::FailedConversion)
+ }
+
+ /// Like [`value()`][File::value()], but returning an `None` if the value wasn't found at `section[.subsection].key`
+ pub fn try_value<'a, T: TryFrom<Cow<'a, BStr>>>(
+ &'a self,
+ section_name: &str,
+ subsection_name: Option<&BStr>,
+ key: &str,
+ ) -> Option<Result<T, T::Error>> {
+ self.raw_value(section_name, subsection_name, key).ok().map(T::try_from)
+ }
+
+ /// Returns all interpreted values given a section, an optional subsection
+ /// and key.
+ ///
+ /// It's recommended to use one of the value types provide dby this crate
+ /// as they implement the conversion, but this function is flexible and
+ /// will accept any type that implements [`TryFrom<&BStr>`][std::convert::TryFrom].
+ ///
+ /// Consider [`Self::value`] if you want to get a single value
+ /// (following last-one-wins resolution) instead.
+ ///
+ /// To access plain strings, use the [`strings()`][Self::strings()] method instead.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use gix_config::{Integer, Boolean};
+ /// # use std::borrow::Cow;
+ /// # use std::convert::TryFrom;
+ /// # use bstr::ByteSlice;
+ /// let config = r#"
+ /// [core]
+ /// a = true
+ /// c
+ /// [core]
+ /// a
+ /// a = false
+ /// "#;
+ /// let gix_config = gix_config::File::try_from(config).unwrap();
+ /// // You can either use the turbofish to determine the type...
+ /// let a_value = gix_config.values::<Boolean>("core", None, "a")?;
+ /// assert_eq!(
+ /// a_value,
+ /// vec![
+ /// Boolean(true),
+ /// Boolean(false),
+ /// Boolean(false),
+ /// ]
+ /// );
+ /// // ... or explicitly declare the type to avoid the turbofish
+ /// let c_value: Vec<Boolean> = gix_config.values("core", None, "c").unwrap();
+ /// assert_eq!(c_value, vec![Boolean(false)]);
+ /// # Ok::<(), Box<dyn std::error::Error>>(())
+ /// ```
+ ///
+ /// [`value`]: crate::value
+ /// [`TryFrom`]: std::convert::TryFrom
+ pub fn values<'a, T: TryFrom<Cow<'a, BStr>>>(
+ &'a self,
+ section_name: &str,
+ subsection_name: Option<&BStr>,
+ key: &str,
+ ) -> Result<Vec<T>, lookup::Error<T::Error>> {
+ self.raw_values(section_name, subsection_name, key)?
+ .into_iter()
+ .map(T::try_from)
+ .collect::<Result<Vec<_>, _>>()
+ .map_err(lookup::Error::FailedConversion)
+ }
+
+ /// Returns the last found immutable section with a given `name` and optional `subsection_name`.
+ pub fn section(
+ &self,
+ name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ ) -> Result<&file::Section<'event>, lookup::existing::Error> {
+ self.section_filter(name, subsection_name, &mut |_| true)?
+ .ok_or(lookup::existing::Error::SectionMissing)
+ }
+
+ /// Returns the last found immutable section with a given `key`, identifying the name and subsection name like `core`
+ /// or `remote.origin`.
+ pub fn section_by_key<'a>(
+ &self,
+ key: impl Into<&'a BStr>,
+ ) -> Result<&file::Section<'event>, lookup::existing::Error> {
+ let key = crate::parse::section::unvalidated::Key::parse(key).ok_or(lookup::existing::Error::KeyMissing)?;
+ self.section(key.section_name, key.subsection_name)
+ }
+
+ /// Returns the last found immutable section with a given `name` and optional `subsection_name`, that matches `filter`.
+ ///
+ /// If there are sections matching `section_name` and `subsection_name` but the `filter` rejects all of them, `Ok(None)`
+ /// is returned.
+ pub fn section_filter<'a>(
+ &'a self,
+ name: impl AsRef<str>,
+ subsection_name: Option<&BStr>,
+ filter: &mut MetadataFilter,
+ ) -> Result<Option<&'a file::Section<'event>>, lookup::existing::Error> {
+ Ok(self
+ .section_ids_by_name_and_subname(name.as_ref(), subsection_name)?
+ .rev()
+ .find_map({
+ let sections = &self.sections;
+ move |id| {
+ let s = &sections[&id];
+ filter(s.meta()).then_some(s)
+ }
+ }))
+ }
+
+ /// Like [`section_filter()`][File::section_filter()], but identifies the section with `key` like `core` or `remote.origin`.
+ pub fn section_filter_by_key<'a, 'b>(
+ &'a self,
+ key: impl Into<&'b BStr>,
+ filter: &mut MetadataFilter,
+ ) -> Result<Option<&'a file::Section<'event>>, lookup::existing::Error> {
+ let key = crate::parse::section::unvalidated::Key::parse(key).ok_or(lookup::existing::Error::KeyMissing)?;
+ self.section_filter(key.section_name, key.subsection_name, filter)
+ }
+
+ /// Gets all sections that match the provided `name`, ignoring any subsections.
+ ///
+ /// # Examples
+ ///
+ /// Provided the following config:
+ ///
+ /// ```text
+ /// [core]
+ /// a = b
+ /// [core ""]
+ /// c = d
+ /// [core "apple"]
+ /// e = f
+ /// ```
+ ///
+ /// Calling this method will yield all sections:
+ ///
+ /// ```
+ /// # use gix_config::File;
+ /// # use gix_config::{Integer, Boolean};
+ /// # use std::borrow::Cow;
+ /// # use std::convert::TryFrom;
+ /// let config = r#"
+ /// [core]
+ /// a = b
+ /// [core ""]
+ /// c = d
+ /// [core "apple"]
+ /// e = f
+ /// "#;
+ /// let gix_config = gix_config::File::try_from(config)?;
+ /// assert_eq!(gix_config.sections_by_name("core").map_or(0, |s|s.count()), 3);
+ /// # Ok::<(), Box<dyn std::error::Error>>(())
+ /// ```
+ #[must_use]
+ pub fn sections_by_name<'a>(&'a self, name: &'a str) -> Option<impl Iterator<Item = &file::Section<'event>> + '_> {
+ self.section_ids_by_name(name).ok().map(move |ids| {
+ ids.map(move |id| {
+ self.sections
+ .get(&id)
+ .expect("section doesn't have id from from lookup")
+ })
+ })
+ }
+
+ /// Similar to [`sections_by_name()`][Self::sections_by_name()], but returns an identifier for this section as well to allow
+ /// referring to it unambiguously even in the light of deletions.
+ #[must_use]
+ pub fn sections_and_ids_by_name<'a>(
+ &'a self,
+ name: &'a str,
+ ) -> Option<impl Iterator<Item = (&file::Section<'event>, SectionId)> + '_> {
+ self.section_ids_by_name(name).ok().map(move |ids| {
+ ids.map(move |id| {
+ (
+ self.sections
+ .get(&id)
+ .expect("section doesn't have id from from lookup"),
+ id,
+ )
+ })
+ })
+ }
+
+ /// Gets all sections that match the provided `name`, ignoring any subsections, and pass the `filter`.
+ #[must_use]
+ pub fn sections_by_name_and_filter<'a>(
+ &'a self,
+ name: &'a str,
+ filter: &'a mut MetadataFilter,
+ ) -> Option<impl Iterator<Item = &file::Section<'event>> + '_> {
+ self.section_ids_by_name(name).ok().map(move |ids| {
+ ids.filter_map(move |id| {
+ let s = self
+ .sections
+ .get(&id)
+ .expect("section doesn't have id from from lookup");
+ filter(s.meta()).then_some(s)
+ })
+ })
+ }
+
+ /// Returns the number of values in the config, no matter in which section.
+ ///
+ /// For example, a config with multiple empty sections will return 0.
+ /// This ignores any comments.
+ #[must_use]
+ pub fn num_values(&self) -> usize {
+ self.sections.values().map(|section| section.num_values()).sum()
+ }
+
+ /// Returns if there are no entries in the config. This will return true
+ /// if there are only empty sections, with whitespace and comments not being considered
+ /// void.
+ #[must_use]
+ pub fn is_void(&self) -> bool {
+ self.sections.values().all(|s| s.body.is_void())
+ }
+
+ /// Return this file's metadata, typically set when it was first created to indicate its origins.
+ ///
+ /// It will be used in all newly created sections to identify them.
+ /// Change it with [`File::set_meta()`].
+ pub fn meta(&self) -> &Metadata {
+ &self.meta
+ }
+
+ /// Change the origin of this instance to be the given `meta`data.
+ ///
+ /// This is useful to control what origin about-to-be-added sections receive.
+ pub fn set_meta(&mut self, meta: impl Into<OwnShared<Metadata>>) -> &mut Self {
+ self.meta = meta.into();
+ self
+ }
+
+ /// Similar to [`meta()`][File::meta()], but with shared ownership.
+ pub fn meta_owned(&self) -> OwnShared<Metadata> {
+ OwnShared::clone(&self.meta)
+ }
+
+ /// Return an iterator over all sections, in order of occurrence in the file itself.
+ pub fn sections(&self) -> impl Iterator<Item = &file::Section<'event>> + '_ {
+ self.section_order.iter().map(move |id| &self.sections[id])
+ }
+
+ /// Return an iterator over all sections and their ids, in order of occurrence in the file itself.
+ pub fn sections_and_ids(&self) -> impl Iterator<Item = (&file::Section<'event>, SectionId)> + '_ {
+ self.section_order.iter().map(move |id| (&self.sections[id], *id))
+ }
+
+ /// Return an iterator over all sections along with non-section events that are placed right after them,
+ /// in order of occurrence in the file itself.
+ ///
+ /// This allows to reproduce the look of sections perfectly when serializing them with
+ /// [`write_to()`][file::Section::write_to()].
+ pub fn sections_and_postmatter(&self) -> impl Iterator<Item = (&file::Section<'event>, Vec<&Event<'event>>)> {
+ self.section_order.iter().map(move |id| {
+ let s = &self.sections[id];
+ let pm: Vec<_> = self
+ .frontmatter_post_section
+ .get(id)
+ .map(|events| events.iter().collect())
+ .unwrap_or_default();
+ (s, pm)
+ })
+ }
+
+ /// Return all events which are in front of the first of our sections, or `None` if there are none.
+ pub fn frontmatter(&self) -> Option<impl Iterator<Item = &Event<'event>>> {
+ (!self.frontmatter_events.is_empty()).then(|| self.frontmatter_events.iter())
+ }
+
+ /// Return the newline characters that have been detected in this config file or the default ones
+ /// for the current platform.
+ ///
+ /// Note that the first found newline is the one we use in the assumption of consistency.
+ pub fn detect_newline_style(&self) -> &BStr {
+ self.frontmatter_events
+ .iter()
+ .find_map(extract_newline)
+ .or_else(|| {
+ self.sections()
+ .find_map(|s| s.body.as_ref().iter().find_map(extract_newline))
+ })
+ .unwrap_or_else(|| platform_newline())
+ }
+
+ pub(crate) fn detect_newline_style_smallvec(&self) -> SmallVec<[u8; 2]> {
+ self.detect_newline_style().as_bytes().into()
+ }
+}