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
}
}
impl<'a> Section<'a> {
pub fn new(
name: impl Into<Cow<'a, str>>,
subsection: impl Into<Option<Cow<'a, BStr>>>,
meta: impl Into<OwnShared<file::Metadata>>,
) -> Result<Self, parse::section::header::Error> {
Ok(Section {
header: parse::section::Header::new(name, subsection)?,
body: Default::default(),
meta: meta.into(),
id: SectionId::default(),
})
}
}
impl<'a> Section<'a> {
pub fn header(&self) -> §ion::Header<'a> {
&self.header
}
pub fn id(&self) -> SectionId {
self.id
}
pub fn body(&self) -> &Body<'a> {
&self.body
}
#[must_use]
pub fn to_bstring(&self) -> BString {
let mut buf = Vec::new();
self.write_to(&mut buf).expect("io error impossible");
buf.into()
}
pub fn write_to(&self, mut out: &mut dyn 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(())
}
pub fn meta(&self) -> &Metadata {
&self.meta
}
pub fn to_mut(&mut self, newline: SmallVec<[u8; 2]>) -> SectionMut<'_, 'a> {
SectionMut::new(self, newline)
}
}