diff options
Diffstat (limited to 'vendor/gix-config/src/file/section/body.rs')
-rw-r--r-- | vendor/gix-config/src/file/section/body.rs | 206 |
1 files changed, 206 insertions, 0 deletions
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<str>) -> Option<Cow<'_, BStr>> { + 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<str>) -> Option<Option<Cow<'_, BStr>>> { + 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<str>) -> Vec<Cow<'_, BStr>> { + 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<Item = &Key<'event>> { + 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<str>) -> 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<usize>, Option<Range<usize>>)> { + 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<Self::Item> { + 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<'_> {} |