use std::{borrow::Cow, iter::FusedIterator, ops::Range, slice};
use bstr::{BStr, BString, ByteSlice, ByteVec};
use crate::{
file::write::extract_newline,
parse::{Event, section::ValueName},
value::normalize,
};
#[derive(Clone, Debug, Default)]
pub(crate) struct BodyData(pub(crate) Vec<Event>);
#[derive(Copy, Clone, Debug)]
pub struct BodyRef<'a> {
pub(crate) body: &'a BodyData,
pub(crate) backing: &'a [u8],
}
impl BodyRef<'_> {
#[must_use]
pub fn value(&self, value_name: impl AsRef<str>) -> Option<BString> {
self.value_implicit(value_name.as_ref()).flatten()
}
#[must_use]
pub fn value_implicit(&self, value_name: &str) -> Option<Option<BString>> {
self.body.value_implicit_in(self.backing, value_name)
}
#[must_use]
pub fn values(&self, value_name: &str) -> Vec<BString> {
self.body.values_in(self.backing, value_name)
}
pub fn value_names(&self) -> impl Iterator<Item = String> + '_ {
self.body.0.iter().filter_map(move |e| match e {
Event::SectionValueName(k) => Some(
k.as_bstr_in(self.backing)
.to_str()
.expect("parsed value names are ASCII")
.to_owned(),
),
_ => None,
})
}
#[must_use]
pub fn contains_value_name(&self, value_name: &str) -> bool {
self.body.contains_value_name_in(self.backing, value_name)
}
#[must_use]
pub fn num_values(&self) -> usize {
self.body.num_values()
}
#[must_use]
pub fn is_void(&self) -> bool {
self.body.is_void()
}
}
impl BodyData {
pub(crate) fn value_implicit_in(&self, backing: &[u8], value_name: &str) -> Option<Option<BString>> {
let key = ValueName::from_str_unchecked(value_name);
let (_key_range, range) = self.key_and_value_range_by_in(backing, &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(v.as_slice_in(backing)).into_owned()));
}
Event::ValueNotDone(v) => {
concatenated.push_str(v.as_slice_in(backing));
}
Event::ValueDone(v) => {
concatenated.push_str(v.as_slice_in(backing));
return Some(Some(normalize(&concatenated).into_owned()));
}
_ => (),
}
}
None
}
pub(crate) fn values_in(&self, backing: &[u8], value_name: &str) -> Vec<BString> {
let key = &ValueName::from_str_unchecked(value_name);
let mut values = Vec::new();
let mut expect_value = false;
let mut concatenated_value = BString::default();
for event in &self.0 {
match event {
Event::SectionValueName(event_key)
if event_key.as_bstr_in(backing).eq_ignore_ascii_case(key.0.as_slice()) =>
{
expect_value = true;
}
Event::Value(v) if expect_value => {
expect_value = false;
values.push(normalize(v.as_slice_in(backing)).into_owned());
}
Event::ValueNotDone(v) if expect_value => {
concatenated_value.push_str(v.as_slice_in(backing));
}
Event::ValueDone(v) if expect_value => {
expect_value = false;
concatenated_value.push_str(v.as_slice_in(backing));
let concatenated_value = std::mem::take(&mut concatenated_value);
values.push(normalize(&concatenated_value).into_owned());
}
_ => (),
}
}
values
}
pub(crate) fn contains_value_name_in(&self, backing: &[u8], value_name: &str) -> bool {
let key = &ValueName::from_str_unchecked(value_name);
self.0.iter().any(|e| {
matches!(e,
Event::SectionValueName(k) if k.as_bstr_in(backing).eq_ignore_ascii_case(key.0.as_slice())
)
})
}
#[must_use]
pub fn num_values(&self) -> usize {
self.0
.iter()
.filter(|e| matches!(e, Event::SectionValueName(_)))
.count()
}
#[must_use]
pub fn is_void(&self) -> bool {
self.0.is_empty()
}
}
impl BodyData {
pub(crate) fn as_ref(&self) -> &[Event] {
&self.0
}
pub(crate) fn detect_newline_style_in<'a>(&'a self, backing: &'a [u8]) -> Option<&'a BStr> {
self.0.iter().find_map(|event| extract_newline(event, backing))
}
pub(crate) fn key_and_value_range_by_in(
&self,
backing: &[u8],
value_name: &ValueName,
) -> 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::SectionValueName(k) => {
if k.as_bstr_in(backing).eq_ignore_ascii_case(value_name.0.as_slice()) {
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| {
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))
})
}
pub(crate) fn copy_to_backing_in(
&self,
source: &[u8],
target: &mut Vec<u8>,
) -> Result<Self, crate::parse::span::Error> {
Ok(BodyData(
self.0
.iter()
.map(|event| event.copy_to_backing_in(source, target))
.collect::<Result<_, _>>()?,
))
}
}
pub struct BodyRefIter<'a> {
iter: slice::Iter<'a, Event>,
backing: &'a [u8],
}
impl<'a> IntoIterator for BodyRef<'a> {
type Item = (String, BString);
type IntoIter = BodyRefIter<'a>;
fn into_iter(self) -> Self::IntoIter {
BodyRefIter {
iter: self.body.0.iter(),
backing: self.backing,
}
}
}
impl Iterator for BodyRefIter<'_> {
type Item = (String, BString);
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.iter.by_ref() {
match event {
Event::SectionValueName(k) => {
key = Some(
k.as_bstr_in(self.backing)
.to_str()
.expect("parsed value names are ASCII")
.to_owned(),
);
}
Event::Value(v) => {
value = Some(Cow::Borrowed(v.as_bstr_in(self.backing)));
break;
}
Event::ValueNotDone(v) => partial_value.push_str(v.as_slice_in(self.backing)),
Event::ValueDone(v) => {
partial_value.push_str(v.as_slice_in(self.backing));
value = Some(Cow::Owned(partial_value));
break;
}
_ => (),
}
}
key.zip(value.map(|value| normalize(value.as_ref()).into_owned()))
}
}
impl FusedIterator for BodyRefIter<'_> {}