diff options
Diffstat (limited to 'vendor/gix-config/src/file/access/raw.rs')
-rw-r--r-- | vendor/gix-config/src/file/access/raw.rs | 536 |
1 files changed, 536 insertions, 0 deletions
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(§ion_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(§ion_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(§ion_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(§ion_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(§ion_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)) + } +} |