114 lines · 3.8 KB · Rust
RawDownload
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(&section))            {                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(&section) {                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()}