From 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:41:41 +0200 Subject: Merging upstream version 1.70.0+dfsg2. Signed-off-by: Daniel Baumann --- vendor/gix-config/src/file/section/body.rs | 206 +++++++++++++++++++++++++++++ vendor/gix-config/src/file/section/mod.rs | 145 ++++++++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 vendor/gix-config/src/file/section/body.rs create mode 100644 vendor/gix-config/src/file/section/mod.rs (limited to 'vendor/gix-config/src/file/section') diff --git a/vendor/gix-config/src/file/section/body.rs b/vendor/gix-config/src/file/section/body.rs new file mode 100644 index 000000000..e1a53efd9 --- /dev/null +++ b/vendor/gix-config/src/file/section/body.rs @@ -0,0 +1,206 @@ +use std::{borrow::Cow, iter::FusedIterator, ops::Range}; + +use bstr::{BStr, BString, ByteVec}; + +use crate::{ + parse::{section::Key, Event}, + value::{normalize, normalize_bstr, normalize_bstring}, +}; + +/// A opaque type that represents a section body. +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Debug, Default)] +pub struct Body<'event>(pub(crate) crate::parse::section::Events<'event>); + +/// Access +impl<'event> Body<'event> { + /// Retrieves the last matching value in a section with the given key, if present. + /// + /// Note that we consider values without key separator `=` non-existing. + #[must_use] + pub fn value(&self, key: impl AsRef) -> Option> { + self.value_implicit(key).flatten() + } + + /// Retrieves the last matching value in a section with the given key, if present, and indicates an implicit value with `Some(None)`, + /// and a non-existing one as `None` + #[must_use] + pub fn value_implicit(&self, key: impl AsRef) -> Option>> { + let key = Key::from_str_unchecked(key.as_ref()); + let (_key_range, range) = self.key_and_value_range_by(&key)?; + let range = match range { + None => return Some(None), + Some(range) => range, + }; + let mut concatenated = BString::default(); + + for event in &self.0[range] { + match event { + Event::Value(v) => { + return Some(Some(normalize_bstr(v.as_ref()))); + } + Event::ValueNotDone(v) => { + concatenated.push_str(v.as_ref()); + } + Event::ValueDone(v) => { + concatenated.push_str(v.as_ref()); + return Some(Some(normalize_bstring(concatenated))); + } + _ => (), + } + } + None + } + + /// Retrieves all values that have the provided key name. This may return + /// an empty vec, which implies there were no values with the provided key. + #[must_use] + pub fn values(&self, key: impl AsRef) -> Vec> { + let key = &Key::from_str_unchecked(key.as_ref()); + let mut values = Vec::new(); + let mut expect_value = false; + let mut concatenated_value = BString::default(); + + for event in &self.0 { + match event { + Event::SectionKey(event_key) if event_key == 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))); + } + _ => (), + } + } + + values + } + + /// Returns an iterator visiting all keys in order. + pub fn keys(&self) -> impl Iterator> { + self.0.iter().filter_map(|e| match e { + Event::SectionKey(k) => Some(k), + _ => None, + }) + } + + /// Returns true if the section contains the provided key. + #[must_use] + pub fn contains_key(&self, key: impl AsRef) -> bool { + let key = &Key::from_str_unchecked(key.as_ref()); + self.0.iter().any(|e| { + matches!(e, + Event::SectionKey(k) if k == key + ) + }) + } + + /// Returns the number of values in the section. + #[must_use] + pub fn num_values(&self) -> usize { + self.0.iter().filter(|e| matches!(e, Event::SectionKey(_))).count() + } + + /// Returns if the section is empty. + /// Note that this may count whitespace, see [`num_values()`][Self::num_values()] for + /// another way to determine semantic emptiness. + #[must_use] + pub fn is_void(&self) -> bool { + self.0.is_empty() + } +} + +impl<'event> Body<'event> { + pub(crate) fn as_ref(&self) -> &[Event<'_>] { + &self.0 + } + + /// Returns the the range containing the value events for the `key`, with value range being `None` if there is no key-value separator + /// and only a 'fake' Value event with an empty string in side. + /// If the value is not found, `None` is returned. + pub(crate) fn key_and_value_range_by(&self, key: &Key<'_>) -> Option<(Range, Option>)> { + let mut value_range = Range::default(); + let mut key_start = None; + for (i, e) in self.0.iter().enumerate().rev() { + match e { + Event::SectionKey(k) => { + if k == key { + key_start = Some(i); + break; + } + value_range = Range::default(); + } + Event::Value(_) => { + (value_range.start, value_range.end) = (i, i); + } + Event::ValueNotDone(_) | Event::ValueDone(_) => { + if value_range.end == 0 { + value_range.end = i + } else { + value_range.start = i + }; + } + _ => (), + } + } + key_start.map(|key_start| { + // value end needs to be offset by one so that the last value's index + // is included in the range + let value_range = value_range.start..value_range.end + 1; + let key_range = key_start..value_range.end; + (key_range, (value_range.start != key_start + 1).then_some(value_range)) + }) + } +} + +/// An owning iterator of a section body. Created by [`Body::into_iter`], yielding +/// un-normalized (`key`, `value`) pairs. +// TODO: tests +pub struct BodyIter<'event>(smallvec::IntoIter<[Event<'event>; 64]>); + +impl<'event> IntoIterator for Body<'event> { + type Item = (Key<'event>, Cow<'event, BStr>); + + type IntoIter = BodyIter<'event>; + + fn into_iter(self) -> Self::IntoIter { + BodyIter(self.0.into_iter()) + } +} + +impl<'event> Iterator for BodyIter<'event> { + type Item = (Key<'event>, Cow<'event, BStr>); + + fn next(&mut self) -> Option { + let mut key = None; + let mut partial_value = BString::default(); + let mut value = None; + + for event in self.0.by_ref() { + match event { + Event::SectionKey(k) => key = Some(k), + Event::Value(v) => { + value = Some(v); + break; + } + Event::ValueNotDone(v) => partial_value.push_str(v.as_ref()), + Event::ValueDone(v) => { + partial_value.push_str(v.as_ref()); + value = Some(partial_value.into()); + break; + } + _ => (), + } + } + + key.zip(value.map(normalize)) + } +} + +impl FusedIterator for BodyIter<'_> {} diff --git a/vendor/gix-config/src/file/section/mod.rs b/vendor/gix-config/src/file/section/mod.rs new file mode 100644 index 000000000..e8e331084 --- /dev/null +++ b/vendor/gix-config/src/file/section/mod.rs @@ -0,0 +1,145 @@ +use std::{borrow::Cow, ops::Deref}; + +use bstr::{BStr, BString, ByteSlice}; +use smallvec::SmallVec; + +use crate::{ + file, + file::{Metadata, Section, SectionMut}, + parse, + parse::{section, Event}, +}; + +pub(crate) mod body; +pub use body::{Body, BodyIter}; +use gix_features::threading::OwnShared; + +use crate::file::{ + write::{extract_newline, platform_newline}, + SectionId, +}; + +impl<'a> Deref for Section<'a> { + type Target = Body<'a>; + + fn deref(&self) -> &Self::Target { + &self.body + } +} + +/// Instantiation and conversion +impl<'a> Section<'a> { + /// Create a new section with the given `name` and optional, `subsection`, `meta`-data and an empty body. + pub fn new( + name: impl Into>, + subsection: impl Into>>, + meta: impl Into>, + ) -> Result { + Ok(Section { + header: parse::section::Header::new(name, subsection)?, + body: Default::default(), + meta: meta.into(), + id: SectionId::default(), + }) + } +} + +/// Access +impl<'a> Section<'a> { + /// Return our header. + pub fn header(&self) -> §ion::Header<'a> { + &self.header + } + + /// Return the unique `id` of the section, for use with the `*_by_id()` family of methods + /// in [gix_config::File][crate::File]. + pub fn id(&self) -> SectionId { + self.id + } + + /// Return our body, containing all keys and values. + pub fn body(&self) -> &Body<'a> { + &self.body + } + + /// Serialize this type into a `BString` for convenience. + /// + /// Note that `to_string()` can also be used, but might not be lossless. + #[must_use] + pub fn to_bstring(&self) -> BString { + let mut buf = Vec::new(); + self.write_to(&mut buf).expect("io error impossible"); + buf.into() + } + + /// Stream ourselves to the given `out`, in order to reproduce this section mostly losslessly + /// as it was parsed. + pub fn write_to(&self, mut out: impl std::io::Write) -> std::io::Result<()> { + self.header.write_to(&mut out)?; + + if self.body.0.is_empty() { + return Ok(()); + } + + let nl = self + .body + .as_ref() + .iter() + .find_map(extract_newline) + .unwrap_or_else(|| platform_newline()); + + if !self + .body + .as_ref() + .iter() + .take_while(|e| !matches!(e, Event::SectionKey(_))) + .any(|e| e.to_bstr_lossy().contains_str(nl)) + { + out.write_all(nl)?; + } + + let mut saw_newline_after_value = true; + let mut in_key_value_pair = false; + for (idx, event) in self.body.as_ref().iter().enumerate() { + match event { + Event::SectionKey(_) => { + if !saw_newline_after_value { + out.write_all(nl)?; + } + saw_newline_after_value = false; + in_key_value_pair = true; + } + Event::Newline(_) if !in_key_value_pair => { + saw_newline_after_value = true; + } + Event::Value(_) | Event::ValueDone(_) => { + in_key_value_pair = false; + } + _ => {} + } + event.write_to(&mut out)?; + if let Event::ValueNotDone(_) = event { + if self + .body + .0 + .get(idx + 1) + .filter(|e| matches!(e, Event::Newline(_))) + .is_none() + { + out.write_all(nl)?; + } + } + } + Ok(()) + } + + /// Return additional information about this sections origin. + pub fn meta(&self) -> &Metadata { + &self.meta + } + + /// Returns a mutable version of this section for adjustment of values. + pub fn to_mut(&mut self, newline: SmallVec<[u8; 2]>) -> SectionMut<'_, 'a> { + SectionMut::new(self, newline) + } +} -- cgit v1.2.3