summaryrefslogtreecommitdiffstats
path: root/vendor/gix-config/src/file/access/read_only.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-config/src/file/access/read_only.rs')
-rw-r--r--vendor/gix-config/src/file/access/read_only.rs353
1 files changed, 353 insertions, 0 deletions
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()
+ }
+}