use bstr::{BStr, BString, ByteSlice};use crate::{File, file::SectionRef, parse::Event};impl File { /// 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 file mostly losslessly /// as it was parsed, while writing only sections for which `filter` returns true. pub fn write_to_filter( &self, mut out: &mut dyn std::io::Write, mut filter: impl FnMut(&SectionRef<'_>) -> bool, ) -> std::io::Result<()> { let nl = self.detect_newline_style(); { for event in self.frontmatter_events.as_ref() { event.write_to_in(&self.backing, &mut out)?; } if !ends_with_newline(self.frontmatter_events.as_ref(), &self.backing, nl, true) && self .sections .values() .map(|section| SectionRef::from_data(section, &self.backing)) .any(|section| filter(§ion)) { out.write_all(nl)?; } } let mut prev_section_ended_with_newline = true; for section_id in &self.section_order { if !prev_section_ended_with_newline { out.write_all(nl)?; } let section_data = self.sections.get(section_id).expect("known section-id"); let section = SectionRef::from_data(section_data, &self.backing); if !filter(§ion) { continue; } section.write_to(&mut *out)?; prev_section_ended_with_newline = ends_with_newline(section_data.body.0.as_ref(), &self.backing, nl, false); if let Some(post_matter) = self.frontmatter_post_section.get(section_id) { if !prev_section_ended_with_newline { out.write_all(nl)?; } for event in post_matter { event.write_to_in(&self.backing, &mut out)?; } prev_section_ended_with_newline = ends_with_newline(post_matter, &self.backing, nl, prev_section_ended_with_newline); } } if !prev_section_ended_with_newline { out.write_all(nl)?; } Ok(()) } /// Stream ourselves to the given `out`, in order to reproduce this file mostly losslessly /// as it was parsed. pub fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> { self.write_to_filter(out, |_| true) }}pub(crate) fn ends_with_newline( e: &[crate::parse::Event], backing: &[u8], nl: impl AsRef<[u8]>, default: bool,) -> bool { if e.is_empty() { return default; } e.iter() .rev() .take_while(|e| e.to_bstr_lossy_in(backing).iter().all(u8::is_ascii_whitespace)) .find_map(|e| e.to_bstr_lossy_in(backing).contains_str(nl.as_ref()).then_some(true)) .unwrap_or(false)}pub(crate) fn extract_newline<'a>(e: &'a Event, backing: &'a [u8]) -> Option<&'a BStr> { Some(match e { Event::Newline(b) => { let nl = b.as_slice_in(backing); // Newlines are parsed consecutively, be sure we only take the smallest possible variant if nl.contains(&b'\r') { "\r\n".into() } else { "\n".into() } } _ => return None, })}pub(crate) fn platform_newline() -> &'static BStr { if cfg!(windows) { "\r\n" } else { "\n" }.into()}
use bstr::{BStr, BString, ByteSlice};
use crate::{File, file::SectionRef, parse::Event};
impl File {
/// 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 file mostly losslessly
/// as it was parsed, while writing only sections for which `filter` returns true.
pub fn write_to_filter(
&self,
mut out: &mut dyn std::io::Write,
mut filter: impl FnMut(&SectionRef<'_>) -> bool,
) -> std::io::Result<()> {
let nl = self.detect_newline_style();
{
for event in self.frontmatter_events.as_ref() {
event.write_to_in(&self.backing, &mut out)?;
}
if !ends_with_newline(self.frontmatter_events.as_ref(), &self.backing, nl, true)
&& self
.sections
.values()
.map(|section| SectionRef::from_data(section, &self.backing))
.any(|section| filter(§ion))
{
out.write_all(nl)?;
}
}
let mut prev_section_ended_with_newline = true;
for section_id in &self.section_order {
if !prev_section_ended_with_newline {
out.write_all(nl)?;
}
let section_data = self.sections.get(section_id).expect("known section-id");
let section = SectionRef::from_data(section_data, &self.backing);
if !filter(§ion) {
continue;
}
section.write_to(&mut *out)?;
prev_section_ended_with_newline = ends_with_newline(section_data.body.0.as_ref(), &self.backing, nl, false);
if let Some(post_matter) = self.frontmatter_post_section.get(section_id) {
if !prev_section_ended_with_newline {
out.write_all(nl)?;
}
for event in post_matter {
event.write_to_in(&self.backing, &mut out)?;
}
prev_section_ended_with_newline =
ends_with_newline(post_matter, &self.backing, nl, prev_section_ended_with_newline);
}
}
if !prev_section_ended_with_newline {
out.write_all(nl)?;
}
Ok(())
}
/// Stream ourselves to the given `out`, in order to reproduce this file mostly losslessly
/// as it was parsed.
pub fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> {
self.write_to_filter(out, |_| true)
}
}
pub(crate) fn ends_with_newline(
e: &[crate::parse::Event],
backing: &[u8],
nl: impl AsRef<[u8]>,
default: bool,
) -> bool {
if e.is_empty() {
return default;
}
e.iter()
.rev()
.take_while(|e| e.to_bstr_lossy_in(backing).iter().all(u8::is_ascii_whitespace))
.find_map(|e| e.to_bstr_lossy_in(backing).contains_str(nl.as_ref()).then_some(true))
.unwrap_or(false)
}
pub(crate) fn extract_newline<'a>(e: &'a Event, backing: &'a [u8]) -> Option<&'a BStr> {
Some(match e {
Event::Newline(b) => {
let nl = b.as_slice_in(backing);
// Newlines are parsed consecutively, be sure we only take the smallest possible variant
if nl.contains(&b'\r') {
"\r\n".into()
} else {
"\n".into()
}
}
_ => return None,
})
}
pub(crate) fn platform_newline() -> &'static BStr {
if cfg!(windows) { "\r\n" } else { "\n" }.into()
}