Sebastian Thielmake fmt535e9674 years agoHistory
536 lines · 19.3 KB · Rust
RawDownload
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<&str>,        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<&str>,        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 str>,        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 str>,        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 git_config::File;    /// # use std::borrow::Cow;    /// # use std::convert::TryFrom;    /// # use bstr::BStr;    /// # let git_config = git_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();    /// assert_eq!(    ///     git_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<&str>,        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<&str>,        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 git_config::File;    /// # use std::borrow::Cow;    /// # use std::convert::TryFrom;    /// # use bstr::BStr;    /// # let mut git_config = git_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();    /// assert_eq!(    ///     git_config.raw_values("core", None, "a")?,    ///     vec![    ///         Cow::<BStr>::Borrowed("b".into()),    ///         Cow::<BStr>::Borrowed("c".into()),    ///         Cow::<BStr>::Borrowed("d".into())    ///     ]    /// );    ///    /// git_config.raw_values_mut("core", None, "a")?.set_all("g");    ///    /// assert_eq!(    ///     git_config.raw_values("core", None, "a")?,    ///     vec![    ///         Cow::<BStr>::Borrowed("g".into()),    ///         Cow::<BStr>::Borrowed("g".into()),    ///         Cow::<BStr>::Borrowed("g".into())    ///     ],    /// );    /// # Ok::<(), git_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 str>,        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 str>,        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 git_config::File;    /// # use std::borrow::Cow;    /// # use bstr::BStr;    /// # use std::convert::TryFrom;    /// # let mut git_config = git_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();    /// git_config.set_existing_raw_value("core", None, "a", "e")?;    /// assert_eq!(git_config.raw_value("core", None, "a")?, Cow::<BStr>::Borrowed("e".into()));    /// assert_eq!(    ///     git_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<&str>,        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 git_config::File;    /// # use std::borrow::Cow;    /// # use bstr::BStr;    /// # use std::convert::TryFrom;    /// # let mut git_config = git_config::File::try_from("[core]a=b").unwrap();    /// let prev = git_config.set_raw_value("core", None, "a", "e")?;    /// git_config.set_raw_value("core", None, "b", "f")?;    /// assert_eq!(prev.expect("present").as_ref(), "b");    /// assert_eq!(git_config.raw_value("core", None, "a")?, Cow::<BStr>::Borrowed("e".into()));    /// assert_eq!(git_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<&str>,        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<&str>,        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 git_config::File;    /// # use std::borrow::Cow;    /// # use std::convert::TryFrom;    /// # use bstr::BStr;    /// # let mut git_config = git_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();    /// let new_values = vec![    ///     "x",    ///     "y",    ///     "z",    /// ];    /// git_config.set_existing_raw_multi_value("core", None, "a", new_values.into_iter())?;    /// let fetched_config = git_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::<(), git_config::lookup::existing::Error>(())    /// ```    ///    /// Setting less than the number of present values sets the first ones found:    ///    /// ```    /// # use git_config::File;    /// # use std::borrow::Cow;    /// # use std::convert::TryFrom;    /// # use bstr::BStr;    /// # let mut git_config = git_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();    /// let new_values = vec![    ///     "x",    ///     "y",    /// ];    /// git_config.set_existing_raw_multi_value("core", None, "a", new_values.into_iter())?;    /// let fetched_config = git_config.raw_values("core", None, "a")?;    /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("x".into())));    /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("y".into())));

Lines 1–500 of 536.