Skip to main content

gix_config/parse/
event.rs

1use std::fmt::Display;
2
3use bstr::{BStr, BString};
4
5use crate::parse::{Event, EventRef};
6
7impl Event {
8    /// Shift all backing-buffer spans in this event forward by `offset` bytes.
9    pub(crate) fn rebase(&mut self, offset: usize) -> Result<(), crate::parse::span::Error> {
10        match self {
11            Event::Comment(comment) => comment.text.rebase(offset),
12            Event::SectionHeader(header) => header.rebase(offset),
13            Event::SectionValueName(name) => name.rebase(offset),
14            Event::Value(bytes)
15            | Event::Newline(bytes)
16            | Event::ValueNotDone(bytes)
17            | Event::ValueDone(bytes)
18            | Event::Whitespace(bytes) => bytes.rebase(offset),
19            Event::KeyValueSeparator => Ok(()),
20        }
21    }
22
23    pub(crate) fn copy_to_backing_in(
24        &self,
25        source: &[u8],
26        target: &mut Vec<u8>,
27    ) -> Result<Event, crate::parse::span::Error> {
28        Ok(match self {
29            Event::Comment(comment) => Event::Comment(comment.copy_to_backing_in(source, target)?),
30            Event::SectionHeader(header) => Event::SectionHeader(header.copy_to_backing_in(source, target)?),
31            Event::SectionValueName(name) => Event::SectionValueName(name.copy_to_backing_in(source, target)?),
32            Event::Value(bytes) => Event::Value(bytes.copy_to_backing_in(source, target)?),
33            Event::Newline(bytes) => Event::Newline(bytes.copy_to_backing_in(source, target)?),
34            Event::ValueNotDone(bytes) => Event::ValueNotDone(bytes.copy_to_backing_in(source, target)?),
35            Event::ValueDone(bytes) => Event::ValueDone(bytes.copy_to_backing_in(source, target)?),
36            Event::Whitespace(bytes) => Event::Whitespace(bytes.copy_to_backing_in(source, target)?),
37            Event::KeyValueSeparator => Event::KeyValueSeparator,
38        })
39    }
40
41    /// Resolve this event against `backing` without allocating.
42    pub(crate) fn as_ref_in<'a>(&'a self, backing: &'a [u8]) -> EventRef<'a> {
43        match self {
44            Event::Comment(comment) => EventRef::Comment {
45                tag: comment.tag,
46                text: comment.text.as_bstr_in(backing),
47            },
48            Event::SectionHeader(header) => EventRef::SectionHeader {
49                name: header.name.as_bstr_in(backing),
50                separator: header.separator.as_ref().map(|separator| separator.as_bstr_in(backing)),
51                subsection_name: header
52                    .subsection_name
53                    .as_ref()
54                    .map(|subsection_name| subsection_name.value_in(backing)),
55            },
56            Event::SectionValueName(name) => EventRef::SectionValueName(name.as_bstr_in(backing)),
57            Event::Value(bytes) => EventRef::Value(bytes.as_bstr_in(backing)),
58            Event::Newline(bytes) => EventRef::Newline(bytes.as_bstr_in(backing)),
59            Event::ValueNotDone(bytes) => EventRef::ValueNotDone(bytes.as_bstr_in(backing)),
60            Event::ValueDone(bytes) => EventRef::ValueDone(bytes.as_bstr_in(backing)),
61            Event::Whitespace(bytes) => EventRef::Whitespace(bytes.as_bstr_in(backing)),
62            Event::KeyValueSeparator => EventRef::KeyValueSeparator,
63        }
64    }
65
66    /// Return the event's principal byte payload resolved against `backing`, without allocating.
67    ///
68    /// This is lossy for events whose serialized representation consists of multiple pieces:
69    /// section headers omit their brackets, separator, and subsection; comments omit their marker;
70    /// and continued values omit their trailing backslash. Use [`Self::write_to_in()`] when the
71    /// complete serialized event is required.
72    pub(crate) fn to_bstr_lossy_in<'a>(&'a self, backing: &'a [u8]) -> &'a BStr {
73        match self {
74            Self::ValueNotDone(e) | Self::Whitespace(e) | Self::Newline(e) | Self::Value(e) | Self::ValueDone(e) => {
75                e.as_bstr_in(backing)
76            }
77            Self::KeyValueSeparator => "=".into(),
78            Self::SectionValueName(k) => k.as_bstr_in(backing),
79            Self::SectionHeader(h) => h.name.as_bstr_in(backing),
80            Self::Comment(c) => c.text.as_bstr_in(backing),
81        }
82    }
83
84    pub(crate) fn write_to_in(&self, backing: &[u8], out: &mut dyn std::io::Write) -> std::io::Result<()> {
85        match self {
86            Self::ValueNotDone(e) => {
87                out.write_all(e.as_slice_in(backing))?;
88                out.write_all(br"\")
89            }
90            Self::Whitespace(e) | Self::Newline(e) | Self::Value(e) | Self::ValueDone(e) => {
91                out.write_all(e.as_slice_in(backing))
92            }
93            Self::KeyValueSeparator => out.write_all(b"="),
94            Self::SectionValueName(k) => out.write_all(k.as_slice_in(backing)),
95            Self::SectionHeader(h) => h.write_to_in(backing, out),
96            Self::Comment(c) => c.write_to_in(backing, out),
97        }
98    }
99}
100
101impl EventRef<'_> {
102    /// Turn ourselves into the text we represent, lossy.
103    ///
104    /// Note that this mirrors `Event::to_bstr_lossy_in()`.
105    pub fn to_bstr_lossy(&self) -> &BStr {
106        match self {
107            EventRef::ValueNotDone(bytes)
108            | EventRef::Whitespace(bytes)
109            | EventRef::Newline(bytes)
110            | EventRef::Value(bytes)
111            | EventRef::ValueDone(bytes) => bytes,
112            EventRef::KeyValueSeparator => "=".into(),
113            EventRef::SectionValueName(name) => name,
114            EventRef::SectionHeader { name, .. } => name,
115            EventRef::Comment { text, .. } => text,
116        }
117    }
118
119    /// Stream ourselves to the given `out`, reproducing this event mostly losslessly.
120    ///
121    /// Quoted subsection names are the exception: [`EventRef::SectionHeader`] contains the decoded
122    /// subsection name, so this method escapes it again and may normalize its original escape
123    /// spelling. The resulting header has the same subsection name, but may not be byte-for-byte
124    /// identical to the parsed input.
125    pub fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> {
126        match self {
127            EventRef::ValueNotDone(bytes) => {
128                out.write_all(bytes)?;
129                out.write_all(br"\")
130            }
131            EventRef::Whitespace(bytes)
132            | EventRef::Newline(bytes)
133            | EventRef::Value(bytes)
134            | EventRef::ValueDone(bytes) => out.write_all(bytes),
135            EventRef::KeyValueSeparator => out.write_all(b"="),
136            EventRef::SectionValueName(name) => out.write_all(name),
137            EventRef::SectionHeader {
138                name,
139                separator,
140                subsection_name,
141            } => {
142                out.write_all(b"[")?;
143                out.write_all(name)?;
144                if let (Some(separator), Some(subsection_name)) = (separator, subsection_name) {
145                    out.write_all(separator)?;
146                    if *separator == b"." {
147                        out.write_all(subsection_name)?;
148                    } else {
149                        out.write_all(b"\"")?;
150                        crate::parse::section::header::write_escaped_subsection(subsection_name, &mut *out)?;
151                        out.write_all(b"\"")?;
152                    }
153                }
154                out.write_all(b"]")
155            }
156            EventRef::Comment { tag, text } => {
157                out.write_all(&[*tag])?;
158                out.write_all(text)
159            }
160        }
161    }
162}
163
164impl Display for EventRef<'_> {
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        Display::fmt(&BString::from(self), f)
167    }
168}
169
170impl From<&EventRef<'_>> for BString {
171    fn from(event: &EventRef<'_>) -> Self {
172        let mut buf = Vec::new();
173        event.write_to(&mut buf).expect("io error impossible");
174        buf.into()
175    }
176}