summaryrefslogtreecommitdiffstats
path: root/vendor/gix-config/src/file/mutable
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-config/src/file/mutable')
-rw-r--r--vendor/gix-config/src/file/mutable/mod.rs107
-rw-r--r--vendor/gix-config/src/file/mutable/multi_value.rs266
-rw-r--r--vendor/gix-config/src/file/mutable/section.rs316
-rw-r--r--vendor/gix-config/src/file/mutable/value.rs63
4 files changed, 752 insertions, 0 deletions
diff --git a/vendor/gix-config/src/file/mutable/mod.rs b/vendor/gix-config/src/file/mutable/mod.rs
new file mode 100644
index 000000000..ad99e09b9
--- /dev/null
+++ b/vendor/gix-config/src/file/mutable/mod.rs
@@ -0,0 +1,107 @@
+use std::borrow::Cow;
+
+use bstr::{BStr, BString, ByteSlice, ByteVec};
+
+use crate::{file, parse::Event};
+
+pub(crate) mod multi_value;
+pub(crate) mod section;
+pub(crate) mod value;
+
+fn escape_value(value: &BStr) -> BString {
+ let starts_with_whitespace = value.first().map_or(false, |b| b.is_ascii_whitespace());
+ let ends_with_whitespace = value
+ .get(value.len().saturating_sub(1))
+ .map_or(false, |b| b.is_ascii_whitespace());
+ let contains_comment_indicators = value.find_byteset(b";#").is_some();
+ let quote = starts_with_whitespace || ends_with_whitespace || contains_comment_indicators;
+
+ let mut buf: BString = Vec::with_capacity(value.len()).into();
+ if quote {
+ buf.push(b'"');
+ }
+
+ for b in value.iter().copied() {
+ match b {
+ b'\n' => buf.push_str("\\n"),
+ b'\t' => buf.push_str("\\t"),
+ b'"' => buf.push_str("\\\""),
+ b'\\' => buf.push_str("\\\\"),
+ _ => buf.push(b),
+ }
+ }
+
+ if quote {
+ buf.push(b'"');
+ }
+ buf
+}
+
+#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+struct Whitespace<'a> {
+ pre_key: Option<Cow<'a, BStr>>,
+ pre_sep: Option<Cow<'a, BStr>>,
+ post_sep: Option<Cow<'a, BStr>>,
+}
+
+impl Default for Whitespace<'_> {
+ fn default() -> Self {
+ Whitespace {
+ pre_key: Some(b"\t".as_bstr().into()),
+ pre_sep: Some(b" ".as_bstr().into()),
+ post_sep: Some(b" ".as_bstr().into()),
+ }
+ }
+}
+
+impl<'a> Whitespace<'a> {
+ fn key_value_separators(&self) -> Vec<Event<'a>> {
+ let mut out = Vec::with_capacity(3);
+ if let Some(ws) = &self.pre_sep {
+ out.push(Event::Whitespace(ws.clone()));
+ }
+ out.push(Event::KeyValueSeparator);
+ if let Some(ws) = &self.post_sep {
+ out.push(Event::Whitespace(ws.clone()));
+ }
+ out
+ }
+
+ fn from_body(s: &file::section::Body<'a>) -> Self {
+ let key_pos =
+ s.0.iter()
+ .enumerate()
+ .find_map(|(idx, e)| matches!(e, Event::SectionKey(_)).then(|| idx));
+ key_pos
+ .map(|key_pos| {
+ let pre_key = s.0[..key_pos].iter().rev().next().and_then(|e| match e {
+ Event::Whitespace(s) => Some(s.clone()),
+ _ => None,
+ });
+ let from_key = &s.0[key_pos..];
+ let (pre_sep, post_sep) = from_key
+ .iter()
+ .enumerate()
+ .find_map(|(idx, e)| matches!(e, Event::KeyValueSeparator).then(|| idx))
+ .map(|sep_pos| {
+ (
+ from_key.get(sep_pos - 1).and_then(|e| match e {
+ Event::Whitespace(ws) => Some(ws.clone()),
+ _ => None,
+ }),
+ from_key.get(sep_pos + 1).and_then(|e| match e {
+ Event::Whitespace(ws) => Some(ws.clone()),
+ _ => None,
+ }),
+ )
+ })
+ .unwrap_or_default();
+ Whitespace {
+ pre_key,
+ pre_sep,
+ post_sep,
+ }
+ })
+ .unwrap_or_default()
+ }
+}
diff --git a/vendor/gix-config/src/file/mutable/multi_value.rs b/vendor/gix-config/src/file/mutable/multi_value.rs
new file mode 100644
index 000000000..396b49b6a
--- /dev/null
+++ b/vendor/gix-config/src/file/mutable/multi_value.rs
@@ -0,0 +1,266 @@
+use std::{borrow::Cow, collections::HashMap, ops::DerefMut};
+
+use bstr::{BStr, BString, ByteVec};
+
+use crate::{
+ file::{
+ self,
+ mutable::{escape_value, Whitespace},
+ Section, SectionId,
+ },
+ lookup,
+ parse::{section, Event},
+ value::{normalize_bstr, normalize_bstring},
+};
+
+/// Internal data structure for [`MutableMultiValue`]
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
+pub(crate) struct EntryData {
+ pub(crate) section_id: SectionId,
+ pub(crate) offset_index: usize,
+}
+
+/// An intermediate representation of a mutable multivar obtained from a [`File`][crate::File].
+#[derive(PartialEq, Eq, Debug)]
+pub struct MultiValueMut<'borrow, 'lookup, 'event> {
+ pub(crate) section: &'borrow mut HashMap<SectionId, Section<'event>>,
+ pub(crate) key: section::Key<'lookup>,
+ /// Each entry data struct provides sufficient information to index into
+ /// [`Self::offsets`]. This layer of indirection is used for users to index
+ /// into the offsets rather than leaking the internal data structures.
+ pub(crate) indices_and_sizes: Vec<EntryData>,
+ /// Each offset represents the size of a event slice and whether or not the
+ /// event slice is significant or not. This is used to index into the
+ /// actual section.
+ pub(crate) offsets: HashMap<SectionId, Vec<usize>>,
+}
+
+impl<'borrow, 'lookup, 'event> MultiValueMut<'borrow, 'lookup, 'event> {
+ /// Returns the actual values.
+ pub fn get(&self) -> Result<Vec<Cow<'_, BStr>>, lookup::existing::Error> {
+ let mut expect_value = false;
+ let mut values = Vec::new();
+ let mut concatenated_value = BString::default();
+
+ for EntryData {
+ section_id,
+ offset_index,
+ } in &self.indices_and_sizes
+ {
+ let (offset, size) = MultiValueMut::index_and_size(&self.offsets, *section_id, *offset_index);
+ for event in &self.section.get(section_id).expect("known section id").as_ref()[offset..offset + size] {
+ match event {
+ Event::SectionKey(section_key) if *section_key == self.key => expect_value = true,
+ Event::Value(v) if expect_value => {
+ expect_value = false;
+ values.push(normalize_bstr(v.as_ref()));
+ }
+ Event::ValueNotDone(v) if expect_value => concatenated_value.push_str(v.as_ref()),
+ Event::ValueDone(v) if expect_value => {
+ expect_value = false;
+ concatenated_value.push_str(v.as_ref());
+ values.push(normalize_bstring(std::mem::take(&mut concatenated_value)));
+ }
+ _ => (),
+ }
+ }
+ }
+
+ if values.is_empty() {
+ return Err(lookup::existing::Error::KeyMissing);
+ }
+
+ Ok(values)
+ }
+
+ /// Returns the amount of values within this multivar.
+ #[must_use]
+ pub fn len(&self) -> usize {
+ self.indices_and_sizes.len()
+ }
+
+ /// Returns true if the multivar does not have any values.
+ /// This might occur if the value was deleted but wasn't yet set with a new value.
+ #[must_use]
+ pub fn is_empty(&self) -> bool {
+ self.indices_and_sizes.is_empty()
+ }
+
+ /// Sets the value at the given index.
+ ///
+ /// # Safety
+ ///
+ /// This will panic if the index is out of range.
+ pub fn set_string_at(&mut self, index: usize, value: impl AsRef<str>) {
+ self.set_at(index, value.as_ref());
+ }
+
+ /// Sets the value at the given index.
+ ///
+ /// # Safety
+ ///
+ /// This will panic if the index is out of range.
+ pub fn set_at<'a>(&mut self, index: usize, value: impl Into<&'a BStr>) {
+ let EntryData {
+ section_id,
+ offset_index,
+ } = self.indices_and_sizes[index];
+ MultiValueMut::set_value_inner(
+ &self.key,
+ &mut self.offsets,
+ &mut self.section.get_mut(&section_id).expect("known section id").body,
+ section_id,
+ offset_index,
+ value.into(),
+ );
+ }
+
+ /// Sets all values to the provided ones. Note that this follows [`zip`]
+ /// logic: if the number of values in the input is less than the number of
+ /// values currently existing, then only the first `n` values are modified.
+ /// If more values are provided than there currently are, then the
+ /// remaining values are ignored.
+ ///
+ /// [`zip`]: std::iter::Iterator::zip
+ pub fn set_values<'a, Iter, Item>(&mut self, values: Iter)
+ where
+ Iter: IntoIterator<Item = Item>,
+ Item: Into<&'a BStr>,
+ {
+ for (
+ EntryData {
+ section_id,
+ offset_index,
+ },
+ value,
+ ) in self.indices_and_sizes.iter().zip(values)
+ {
+ Self::set_value_inner(
+ &self.key,
+ &mut self.offsets,
+ &mut self.section.get_mut(section_id).expect("known section id").body,
+ *section_id,
+ *offset_index,
+ value.into(),
+ );
+ }
+ }
+
+ /// Sets all values in this multivar to the provided one without owning the
+ /// provided input.
+ pub fn set_all<'a>(&mut self, input: impl Into<&'a BStr>) {
+ let input = input.into();
+ for EntryData {
+ section_id,
+ offset_index,
+ } in &self.indices_and_sizes
+ {
+ Self::set_value_inner(
+ &self.key,
+ &mut self.offsets,
+ &mut self.section.get_mut(section_id).expect("known section id").body,
+ *section_id,
+ *offset_index,
+ input,
+ );
+ }
+ }
+
+ fn set_value_inner<'a: 'event>(
+ key: &section::Key<'lookup>,
+ offsets: &mut HashMap<SectionId, Vec<usize>>,
+ section: &mut file::section::Body<'event>,
+ section_id: SectionId,
+ offset_index: usize,
+ value: &BStr,
+ ) {
+ let (offset, size) = MultiValueMut::index_and_size(offsets, section_id, offset_index);
+ let whitespace = Whitespace::from_body(section);
+ let section = section.as_mut();
+ section.drain(offset..offset + size);
+
+ let key_sep_events = whitespace.key_value_separators();
+ MultiValueMut::set_offset(offsets, section_id, offset_index, 2 + key_sep_events.len());
+ section.insert(offset, Event::Value(escape_value(value).into()));
+ section.insert_many(offset, key_sep_events.into_iter().rev());
+ section.insert(offset, Event::SectionKey(key.to_owned()));
+ }
+
+ /// Removes the value at the given index. Does nothing when called multiple
+ /// times in succession.
+ ///
+ /// # Safety
+ ///
+ /// This will panic if the index is out of range.
+ pub fn delete(&mut self, index: usize) {
+ let EntryData {
+ section_id,
+ offset_index,
+ } = &self.indices_and_sizes[index];
+ let (offset, size) = MultiValueMut::index_and_size(&self.offsets, *section_id, *offset_index);
+ if size == 0 {
+ return;
+ }
+ self.section
+ .get_mut(section_id)
+ .expect("known section id")
+ .body
+ .as_mut()
+ .drain(offset..offset + size);
+
+ Self::set_offset(&mut self.offsets, *section_id, *offset_index, 0);
+ self.indices_and_sizes.remove(index);
+ }
+
+ /// Removes all values. Does nothing when called multiple times in
+ /// succession.
+ pub fn delete_all(&mut self) {
+ for EntryData {
+ section_id,
+ offset_index,
+ } in &self.indices_and_sizes
+ {
+ let (offset, size) = MultiValueMut::index_and_size(&self.offsets, *section_id, *offset_index);
+ if size == 0 {
+ continue;
+ }
+ self.section
+ .get_mut(section_id)
+ .expect("known section id")
+ .body
+ .as_mut()
+ .drain(offset..offset + size);
+ Self::set_offset(&mut self.offsets, *section_id, *offset_index, 0);
+ }
+ self.indices_and_sizes.clear();
+ }
+
+ fn index_and_size(
+ offsets: &'lookup HashMap<SectionId, Vec<usize>>,
+ section_id: SectionId,
+ offset_index: usize,
+ ) -> (usize, usize) {
+ offsets
+ .get(&section_id)
+ .expect("known section id")
+ .iter()
+ .take(offset_index + 1)
+ .fold((0, 0), |(total_ofs, ofs), size| (total_ofs + ofs, *size))
+ }
+
+ // This must be an associated function rather than a method to allow Rust
+ // to split mutable borrows.
+ fn set_offset(
+ offsets: &mut HashMap<SectionId, Vec<usize>>,
+ section_id: SectionId,
+ offset_index: usize,
+ value: usize,
+ ) {
+ *offsets
+ .get_mut(&section_id)
+ .expect("known section id")
+ .get_mut(offset_index)
+ .unwrap()
+ .deref_mut() = value;
+ }
+}
diff --git a/vendor/gix-config/src/file/mutable/section.rs b/vendor/gix-config/src/file/mutable/section.rs
new file mode 100644
index 000000000..def68ac60
--- /dev/null
+++ b/vendor/gix-config/src/file/mutable/section.rs
@@ -0,0 +1,316 @@
+use std::{
+ borrow::Cow,
+ ops::{Deref, Range},
+};
+
+use bstr::{BStr, BString, ByteSlice, ByteVec};
+use smallvec::SmallVec;
+
+use crate::{
+ file::{
+ self,
+ mutable::{escape_value, Whitespace},
+ Index, Section, Size,
+ },
+ lookup, parse,
+ parse::{section::Key, Event},
+ value::{normalize, normalize_bstr, normalize_bstring},
+};
+
+/// A opaque type that represents a mutable reference to a section.
+#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+pub struct SectionMut<'a, 'event> {
+ section: &'a mut Section<'event>,
+ implicit_newline: bool,
+ whitespace: Whitespace<'event>,
+ newline: SmallVec<[u8; 2]>,
+}
+
+/// Mutating methods.
+impl<'a, 'event> SectionMut<'a, 'event> {
+ /// Adds an entry to the end of this section name `key` and `value`. If `value` is `None`, no equal sign will be written leaving
+ /// just the key. This is useful for boolean values which are true if merely the key exists.
+ pub fn push<'b>(&mut self, key: Key<'event>, value: Option<&'b BStr>) -> &mut Self {
+ self.push_with_comment_inner(key, value, None);
+ self
+ }
+
+ /// Adds an entry to the end of this section name `key` and `value`. If `value` is `None`, no equal sign will be written leaving
+ /// just the key. This is useful for boolean values which are true if merely the key exists.
+ /// `comment` has to be the text to put right after the value and behind a `#` character. Note that newlines are silently transformed
+ /// into spaces.
+ pub fn push_with_comment<'b, 'c>(
+ &mut self,
+ key: Key<'event>,
+ value: Option<&'b BStr>,
+ comment: impl Into<&'c BStr>,
+ ) -> &mut Self {
+ self.push_with_comment_inner(key, value, comment.into().into());
+ self
+ }
+
+ fn push_with_comment_inner(&mut self, key: Key<'event>, value: Option<&BStr>, comment: Option<&BStr>) {
+ let body = &mut self.section.body.0;
+ if let Some(ws) = &self.whitespace.pre_key {
+ body.push(Event::Whitespace(ws.clone()));
+ }
+
+ body.push(Event::SectionKey(key));
+ match value {
+ Some(value) => {
+ body.extend(self.whitespace.key_value_separators());
+ body.push(Event::Value(escape_value(value).into()));
+ }
+ None => body.push(Event::Value(Cow::Borrowed("".into()))),
+ }
+ if let Some(comment) = comment {
+ body.push(Event::Whitespace(Cow::Borrowed(" ".into())));
+ body.push(Event::Comment(parse::Comment {
+ tag: b'#',
+ text: Cow::Owned({
+ let mut c = Vec::with_capacity(comment.len());
+ let mut bytes = comment.iter().peekable();
+ if !bytes.peek().map_or(true, |b| b.is_ascii_whitespace()) {
+ c.insert(0, b' ');
+ }
+ c.extend(bytes.map(|b| if *b == b'\n' { b' ' } else { *b }));
+ c.into()
+ }),
+ }));
+ }
+ if self.implicit_newline {
+ body.push(Event::Newline(BString::from(self.newline.to_vec()).into()));
+ }
+ }
+
+ /// Removes all events until a key value pair is removed. This will also
+ /// remove the whitespace preceding the key value pair, if any is found.
+ pub fn pop(&mut self) -> Option<(Key<'_>, Cow<'event, BStr>)> {
+ let mut values = Vec::new();
+ // events are popped in reverse order
+ let body = &mut self.section.body.0;
+ while let Some(e) = body.pop() {
+ match e {
+ Event::SectionKey(k) => {
+ // pop leading whitespace
+ if let Some(Event::Whitespace(_)) = body.last() {
+ body.pop();
+ }
+
+ if values.len() == 1 {
+ let value = values.pop().expect("vec is non-empty but popped to empty value");
+ return Some((k, normalize(value)));
+ }
+
+ return Some((
+ k,
+ normalize_bstring({
+ let mut s = BString::default();
+ for value in values.into_iter().rev() {
+ s.push_str(value.as_ref());
+ }
+ s
+ }),
+ ));
+ }
+ Event::Value(v) | Event::ValueNotDone(v) | Event::ValueDone(v) => values.push(v),
+ _ => (),
+ }
+ }
+ None
+ }
+
+ /// Sets the last key value pair if it exists, or adds the new value.
+ /// Returns the previous value if it replaced a value, or None if it adds
+ /// the value.
+ pub fn set<'b>(&mut self, key: Key<'event>, value: impl Into<&'b BStr>) -> Option<Cow<'event, BStr>> {
+ match self.key_and_value_range_by(&key) {
+ None => {
+ self.push(key, Some(value.into()));
+ None
+ }
+ Some((key_range, value_range)) => {
+ let value_range = value_range.unwrap_or(key_range.end - 1..key_range.end);
+ let range_start = value_range.start;
+ let ret = self.remove_internal(value_range, false);
+ self.section
+ .body
+ .0
+ .insert(range_start, Event::Value(escape_value(value.into()).into()));
+ Some(ret)
+ }
+ }
+ }
+
+ /// Removes the latest value by key and returns it, if it exists.
+ pub fn remove(&mut self, key: impl AsRef<str>) -> Option<Cow<'event, BStr>> {
+ let key = Key::from_str_unchecked(key.as_ref());
+ let (key_range, _value_range) = self.key_and_value_range_by(&key)?;
+ Some(self.remove_internal(key_range, true))
+ }
+
+ /// Adds a new line event. Note that you don't need to call this unless
+ /// you've disabled implicit newlines.
+ pub fn push_newline(&mut self) -> &mut Self {
+ self.section
+ .body
+ .0
+ .push(Event::Newline(Cow::Owned(BString::from(self.newline.to_vec()))));
+ self
+ }
+
+ /// Return the newline used when calling [`push_newline()`][Self::push_newline()].
+ pub fn newline(&self) -> &BStr {
+ self.newline.as_slice().as_bstr()
+ }
+
+ /// Enables or disables automatically adding newline events after adding
+ /// a value. This is _enabled by default_.
+ pub fn set_implicit_newline(&mut self, on: bool) -> &mut Self {
+ self.implicit_newline = on;
+ self
+ }
+
+ /// Sets the exact whitespace to use before each newly created key-value pair,
+ /// with only whitespace characters being permissible.
+ ///
+ /// The default is 2 tabs.
+ /// Set to `None` to disable adding whitespace before a key value.
+ ///
+ /// # Panics
+ ///
+ /// If non-whitespace characters are used. This makes the method only suitable for validated
+ /// or known input.
+ pub fn set_leading_whitespace(&mut self, whitespace: Option<Cow<'event, BStr>>) -> &mut Self {
+ assert!(
+ whitespace
+ .as_deref()
+ .map_or(true, |ws| ws.iter().all(|b| b.is_ascii_whitespace())),
+ "input whitespace must only contain whitespace characters."
+ );
+ self.whitespace.pre_key = whitespace;
+ self
+ }
+
+ /// Returns the whitespace this section will insert before the
+ /// beginning of a key, if any.
+ #[must_use]
+ pub fn leading_whitespace(&self) -> Option<&BStr> {
+ self.whitespace.pre_key.as_deref()
+ }
+
+ /// Returns the whitespace to be used before and after the `=` between the key
+ /// and the value.
+ ///
+ /// For example, `k = v` will have `(Some(" "), Some(" "))`, whereas `k=\tv` will
+ /// have `(None, Some("\t"))`.
+ #[must_use]
+ pub fn separator_whitespace(&self) -> (Option<&BStr>, Option<&BStr>) {
+ (self.whitespace.pre_sep.as_deref(), self.whitespace.post_sep.as_deref())
+ }
+}
+
+// Internal methods that may require exact indices for faster operations.
+impl<'a, 'event> SectionMut<'a, 'event> {
+ pub(crate) fn new(section: &'a mut Section<'event>, newline: SmallVec<[u8; 2]>) -> Self {
+ let whitespace = Whitespace::from_body(&section.body);
+ Self {
+ section,
+ implicit_newline: true,
+ whitespace,
+ newline,
+ }
+ }
+
+ pub(crate) fn get(
+ &self,
+ key: &Key<'_>,
+ start: Index,
+ end: Index,
+ ) -> Result<Cow<'_, BStr>, lookup::existing::Error> {
+ let mut expect_value = false;
+ let mut concatenated_value = BString::default();
+
+ for event in &self.section.0[start.0..end.0] {
+ match event {
+ Event::SectionKey(event_key) if event_key == key => expect_value = true,
+ Event::Value(v) if expect_value => return Ok(normalize_bstr(v.as_ref())),
+ Event::ValueNotDone(v) if expect_value => {
+ concatenated_value.push_str(v.as_ref());
+ }
+ Event::ValueDone(v) if expect_value => {
+ concatenated_value.push_str(v.as_ref());
+ return Ok(normalize_bstring(concatenated_value));
+ }
+ _ => (),
+ }
+ }
+
+ Err(lookup::existing::Error::KeyMissing)
+ }
+
+ pub(crate) fn delete(&mut self, start: Index, end: Index) {
+ self.section.body.0.drain(start.0..end.0);
+ }
+
+ pub(crate) fn set_internal(&mut self, index: Index, key: Key<'event>, value: &BStr) -> Size {
+ let mut size = 0;
+
+ let body = &mut self.section.body.0;
+ body.insert(index.0, Event::Value(escape_value(value).into()));
+ size += 1;
+
+ let sep_events = self.whitespace.key_value_separators();
+ size += sep_events.len();
+ body.insert_many(index.0, sep_events.into_iter().rev());
+
+ body.insert(index.0, Event::SectionKey(key));
+ size += 1;
+
+ Size(size)
+ }
+
+ /// Performs the removal, assuming the range is valid.
+ fn remove_internal(&mut self, range: Range<usize>, fix_whitespace: bool) -> Cow<'event, BStr> {
+ let events = &mut self.section.body.0;
+ if fix_whitespace
+ && events
+ .get(range.end)
+ .map_or(false, |ev| matches!(ev, Event::Newline(_)))
+ {
+ events.remove(range.end);
+ }
+ let value = events
+ .drain(range.clone())
+ .fold(Cow::Owned(BString::default()), |mut acc: Cow<'_, BStr>, e| {
+ if let Event::Value(v) | Event::ValueNotDone(v) | Event::ValueDone(v) = e {
+ acc.to_mut().extend(&**v);
+ }
+ acc
+ });
+ if fix_whitespace
+ && range
+ .start
+ .checked_sub(1)
+ .and_then(|pos| events.get(pos))
+ .map_or(false, |ev| matches!(ev, Event::Whitespace(_)))
+ {
+ events.remove(range.start - 1);
+ }
+ value
+ }
+}
+
+impl<'event> Deref for SectionMut<'_, 'event> {
+ type Target = file::Section<'event>;
+
+ fn deref(&self) -> &Self::Target {
+ self.section
+ }
+}
+
+impl<'event> file::section::Body<'event> {
+ pub(crate) fn as_mut(&mut self) -> &mut parse::section::Events<'event> {
+ &mut self.0
+ }
+}
diff --git a/vendor/gix-config/src/file/mutable/value.rs b/vendor/gix-config/src/file/mutable/value.rs
new file mode 100644
index 000000000..2bccfd32a
--- /dev/null
+++ b/vendor/gix-config/src/file/mutable/value.rs
@@ -0,0 +1,63 @@
+use std::borrow::Cow;
+
+use bstr::BStr;
+
+use crate::{
+ file,
+ file::{mutable::section::SectionMut, Index, Size},
+ lookup,
+ parse::section,
+};
+
+/// An intermediate representation of a mutable value obtained from a [`File`][crate::File].
+#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+pub struct ValueMut<'borrow, 'lookup, 'event> {
+ pub(crate) section: SectionMut<'borrow, 'event>,
+ pub(crate) key: section::Key<'lookup>,
+ pub(crate) index: Index,
+ pub(crate) size: Size,
+}
+
+impl<'borrow, 'lookup, 'event> ValueMut<'borrow, 'lookup, 'event> {
+ /// Returns the actual value. This is computed each time this is called
+ /// requiring an allocation for multi-line values.
+ pub fn get(&self) -> Result<Cow<'_, BStr>, lookup::existing::Error> {
+ self.section.get(&self.key, self.index, self.index + self.size)
+ }
+
+ /// Update the value to the provided one. This modifies the value such that
+ /// the Value event(s) are replaced with a single new event containing the
+ /// new value.
+ pub fn set_string(&mut self, input: impl AsRef<str>) {
+ self.set(input.as_ref());
+ }
+
+ /// Update the value to the provided one. This modifies the value such that
+ /// the Value event(s) are replaced with a single new event containing the
+ /// new value.
+ pub fn set<'a>(&mut self, input: impl Into<&'a BStr>) {
+ if self.size.0 > 0 {
+ self.section.delete(self.index, self.index + self.size);
+ }
+ self.size = self.section.set_internal(self.index, self.key.to_owned(), input.into());
+ }
+
+ /// Removes the value. Does nothing when called multiple times in
+ /// succession.
+ pub fn delete(&mut self) {
+ if self.size.0 > 0 {
+ self.section.delete(self.index, self.index + self.size);
+ self.size = Size(0);
+ }
+ }
+
+ /// Return the section containing the value.
+ pub fn section(&self) -> &file::Section<'event> {
+ &self.section
+ }
+
+ /// Convert this value into its owning mutable section.
+ pub fn into_section_mut(self) -> file::SectionMut<'borrow, 'event> {
+ self.section
+ }
+}