diff options
Diffstat (limited to 'vendor/gix-config/src/file/mutable')
-rw-r--r-- | vendor/gix-config/src/file/mutable/mod.rs | 107 | ||||
-rw-r--r-- | vendor/gix-config/src/file/mutable/multi_value.rs | 266 | ||||
-rw-r--r-- | vendor/gix-config/src/file/mutable/section.rs | 316 | ||||
-rw-r--r-- | vendor/gix-config/src/file/mutable/value.rs | 63 |
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(§ion_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: §ion::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(§ion_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(§ion_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(§ion.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 + } +} |