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() }}
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()
}
}