176 lines · 7.7 KB · Rust
RawDownload
use std::fmt::Display; use bstr::{BStr, BString}; use crate::parse::{Event, EventRef}; impl Event {    /// Shift all backing-buffer spans in this event forward by `offset` bytes.    pub(crate) fn rebase(&mut self, offset: usize) -> Result<(), crate::parse::span::Error> {        match self {            Event::Comment(comment) => comment.text.rebase(offset),            Event::SectionHeader(header) => header.rebase(offset),            Event::SectionValueName(name) => name.rebase(offset),            Event::Value(bytes)            | Event::Newline(bytes)            | Event::ValueNotDone(bytes)            | Event::ValueDone(bytes)            | Event::Whitespace(bytes) => bytes.rebase(offset),            Event::KeyValueSeparator => Ok(()),        }    }     pub(crate) fn copy_to_backing_in(        &self,        source: &[u8],        target: &mut Vec<u8>,    ) -> Result<Event, crate::parse::span::Error> {        Ok(match self {            Event::Comment(comment) => Event::Comment(comment.copy_to_backing_in(source, target)?),            Event::SectionHeader(header) => Event::SectionHeader(header.copy_to_backing_in(source, target)?),            Event::SectionValueName(name) => Event::SectionValueName(name.copy_to_backing_in(source, target)?),            Event::Value(bytes) => Event::Value(bytes.copy_to_backing_in(source, target)?),            Event::Newline(bytes) => Event::Newline(bytes.copy_to_backing_in(source, target)?),            Event::ValueNotDone(bytes) => Event::ValueNotDone(bytes.copy_to_backing_in(source, target)?),            Event::ValueDone(bytes) => Event::ValueDone(bytes.copy_to_backing_in(source, target)?),            Event::Whitespace(bytes) => Event::Whitespace(bytes.copy_to_backing_in(source, target)?),            Event::KeyValueSeparator => Event::KeyValueSeparator,        })    }     /// Resolve this event against `backing` without allocating.    pub(crate) fn as_ref_in<'a>(&'a self, backing: &'a [u8]) -> EventRef<'a> {        match self {            Event::Comment(comment) => EventRef::Comment {                tag: comment.tag,                text: comment.text.as_bstr_in(backing),            },            Event::SectionHeader(header) => EventRef::SectionHeader {                name: header.name.as_bstr_in(backing),                separator: header.separator.as_ref().map(|separator| separator.as_bstr_in(backing)),                subsection_name: header                    .subsection_name                    .as_ref()                    .map(|subsection_name| subsection_name.value_in(backing)),            },            Event::SectionValueName(name) => EventRef::SectionValueName(name.as_bstr_in(backing)),            Event::Value(bytes) => EventRef::Value(bytes.as_bstr_in(backing)),            Event::Newline(bytes) => EventRef::Newline(bytes.as_bstr_in(backing)),            Event::ValueNotDone(bytes) => EventRef::ValueNotDone(bytes.as_bstr_in(backing)),            Event::ValueDone(bytes) => EventRef::ValueDone(bytes.as_bstr_in(backing)),            Event::Whitespace(bytes) => EventRef::Whitespace(bytes.as_bstr_in(backing)),            Event::KeyValueSeparator => EventRef::KeyValueSeparator,        }    }     /// Return the event's principal byte payload resolved against `backing`, without allocating.    ///    /// This is lossy for events whose serialized representation consists of multiple pieces:    /// section headers omit their brackets, separator, and subsection; comments omit their marker;    /// and continued values omit their trailing backslash. Use [`Self::write_to_in()`] when the    /// complete serialized event is required.    pub(crate) fn to_bstr_lossy_in<'a>(&'a self, backing: &'a [u8]) -> &'a BStr {        match self {            Self::ValueNotDone(e) | Self::Whitespace(e) | Self::Newline(e) | Self::Value(e) | Self::ValueDone(e) => {                e.as_bstr_in(backing)            }            Self::KeyValueSeparator => "=".into(),            Self::SectionValueName(k) => k.as_bstr_in(backing),            Self::SectionHeader(h) => h.name.as_bstr_in(backing),            Self::Comment(c) => c.text.as_bstr_in(backing),        }    }     pub(crate) fn write_to_in(&self, backing: &[u8], out: &mut dyn std::io::Write) -> std::io::Result<()> {        match self {            Self::ValueNotDone(e) => {                out.write_all(e.as_slice_in(backing))?;                out.write_all(br"\")            }            Self::Whitespace(e) | Self::Newline(e) | Self::Value(e) | Self::ValueDone(e) => {                out.write_all(e.as_slice_in(backing))            }            Self::KeyValueSeparator => out.write_all(b"="),            Self::SectionValueName(k) => out.write_all(k.as_slice_in(backing)),            Self::SectionHeader(h) => h.write_to_in(backing, out),            Self::Comment(c) => c.write_to_in(backing, out),        }    }} impl EventRef<'_> {    /// Turn ourselves into the text we represent, lossy.    ///    /// Note that this mirrors `Event::to_bstr_lossy_in()`.    pub fn to_bstr_lossy(&self) -> &BStr {        match self {            EventRef::ValueNotDone(bytes)            | EventRef::Whitespace(bytes)            | EventRef::Newline(bytes)            | EventRef::Value(bytes)            | EventRef::ValueDone(bytes) => bytes,            EventRef::KeyValueSeparator => "=".into(),            EventRef::SectionValueName(name) => name,            EventRef::SectionHeader { name, .. } => name,            EventRef::Comment { text, .. } => text,        }    }     /// Stream ourselves to the given `out`, reproducing this event mostly losslessly.    ///    /// Quoted subsection names are the exception: [`EventRef::SectionHeader`] contains the decoded    /// subsection name, so this method escapes it again and may normalize its original escape    /// spelling. The resulting header has the same subsection name, but may not be byte-for-byte    /// identical to the parsed input.    pub fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> {        match self {            EventRef::ValueNotDone(bytes) => {                out.write_all(bytes)?;                out.write_all(br"\")            }            EventRef::Whitespace(bytes)            | EventRef::Newline(bytes)            | EventRef::Value(bytes)            | EventRef::ValueDone(bytes) => out.write_all(bytes),            EventRef::KeyValueSeparator => out.write_all(b"="),            EventRef::SectionValueName(name) => out.write_all(name),            EventRef::SectionHeader {                name,                separator,                subsection_name,            } => {                out.write_all(b"[")?;                out.write_all(name)?;                if let (Some(separator), Some(subsection_name)) = (separator, subsection_name) {                    out.write_all(separator)?;                    if *separator == b"." {                        out.write_all(subsection_name)?;                    } else {                        out.write_all(b"\"")?;                        crate::parse::section::header::write_escaped_subsection(subsection_name, &mut *out)?;                        out.write_all(b"\"")?;                    }                }                out.write_all(b"]")            }            EventRef::Comment { tag, text } => {                out.write_all(&[*tag])?;                out.write_all(text)            }        }    }} impl Display for EventRef<'_> {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        Display::fmt(&BString::from(self), f)    }} impl From<&EventRef<'_>> for BString {    fn from(event: &EventRef<'_>) -> Self {        let mut buf = Vec::new();        event.write_to(&mut buf).expect("io error impossible");        buf.into()    }}