gix_config/parse/mod.rs
1//! This module handles parsing a `git-config` file. Generally speaking, you
2//! want to use a higher abstraction such as [`File`] unless you have some
3//! explicit reason to work with events instead.
4//!
5//! Use [`Events::from_bytes()`] to obtain a self-contained parsed representation,
6//! then iterate over its event views.
7//!
8//! On a higher level, one can use [`Events`] to parse all events into a set
9//! of easily interpretable data type, similar to what [`File`] does.
10//!
11//! [`File`]: crate::File
12
13use bstr::{BStr, BString, ByteSlice};
14
15mod from_bytes;
16
17mod event;
18#[path = "events.rs"]
19mod events_type;
20pub(crate) use events_type::FrontMatterEvents;
21pub use events_type::{Events, SectionRef};
22mod comment;
23mod error;
24///
25pub mod section;
26
27#[cfg(test)]
28pub(crate) mod tests;
29
30/// A range into a shared backing buffer.
31#[derive(Copy, Clone, Debug, Default)]
32pub(crate) struct Span {
33 start: u32,
34 len: u32,
35}
36
37/// Errors produced when a span cannot be represented.
38pub mod span {
39 /// A span offset or length exceeded the supported 32-bit representation.
40 #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, thiserror::Error)]
41 #[error("configuration data exceeds the supported span size of {} bytes", u32::MAX)]
42 pub struct Error;
43}
44
45/// A raw span whose semantic value may have required decoding while parsing.
46///
47/// The raw bytes are retained for lossless serialization. `decoded` is present only when those
48/// bytes had to be transformed for semantic access, such as an escaped quoted subsection name.
49#[derive(Clone, Debug)]
50pub(crate) struct MaybeDecoded {
51 raw: Span,
52 decoded: Option<BString>,
53}
54
55impl MaybeDecoded {
56 pub(crate) fn raw(raw: Span) -> Self {
57 Self { raw, decoded: None }
58 }
59
60 pub(crate) fn decoded(raw: Span, decoded: BString) -> Self {
61 Self {
62 raw,
63 decoded: Some(decoded),
64 }
65 }
66
67 pub(crate) fn raw_span(&self) -> Span {
68 self.raw
69 }
70
71 pub(crate) fn value_in<'a>(&'a self, backing: &'a [u8]) -> &'a BStr {
72 self.decoded
73 .as_ref()
74 .map_or_else(|| self.raw.as_bstr_in(backing), |value| value.as_bstr())
75 }
76
77 pub(crate) fn rebase(&mut self, offset: usize) -> Result<(), span::Error> {
78 self.raw.rebase(offset)
79 }
80
81 pub(crate) fn copy_to_backing_in(&self, source: &[u8], target: &mut Vec<u8>) -> Result<Self, span::Error> {
82 Ok(Self {
83 raw: self.raw.copy_to_backing_in(source, target)?,
84 decoded: self.decoded.clone(),
85 })
86 }
87}
88
89impl Span {
90 pub(crate) fn append(backing: &mut Vec<u8>, bytes: &[u8]) -> Result<Self, span::Error> {
91 let start = backing.len();
92 let span = Self::range(start, bytes.len())?;
93 backing.len().checked_add(bytes.len()).ok_or(span::Error)?;
94 backing.extend_from_slice(bytes);
95 Ok(span)
96 }
97
98 pub(crate) fn range(start: usize, len: usize) -> Result<Self, span::Error> {
99 Ok(Span {
100 start: start.try_into().map_err(|_| span::Error)?,
101 len: len.try_into().map_err(|_| span::Error)?,
102 })
103 }
104
105 pub(crate) fn new(backing: &[u8], bytes: &[u8]) -> Self {
106 if bytes.is_empty() {
107 return Self::default();
108 }
109 debug_assert!(!backing.is_empty());
110 let base = backing.as_ptr() as usize;
111 let start = (bytes.as_ptr() as usize)
112 .checked_sub(base)
113 .expect("span must point into the backing buffer");
114 let end = start + bytes.len();
115 debug_assert!(end <= backing.len());
116 Self::range(start, bytes.len()).expect("the parser rejects backing buffers that exceed the span limit")
117 }
118
119 /// Return ourselves as byte string slice using `backing` to resolve spans.
120 pub fn as_bstr_in<'a>(&'a self, backing: &'a [u8]) -> &'a BStr {
121 self.as_slice_in(backing).as_bstr()
122 }
123
124 /// Return ourselves as bytes using `backing` to resolve spans.
125 pub fn as_slice_in<'a>(&'a self, backing: &'a [u8]) -> &'a [u8] {
126 let start = self.start as usize;
127 &backing[start..start + self.len as usize]
128 }
129
130 /// Convert into owned bytes using `backing` to resolve spans.
131 pub fn to_bstring_in(self, backing: &[u8]) -> BString {
132 self.as_slice_in(backing).into()
133 }
134
135 pub(crate) fn copy_to_backing_in(&self, source: &[u8], target: &mut Vec<u8>) -> Result<Self, span::Error> {
136 Span::append(target, self.as_slice_in(source))
137 }
138
139 pub(crate) fn rebase(&mut self, offset: usize) -> Result<(), span::Error> {
140 self.start = (self.start as usize)
141 .checked_add(offset)
142 .and_then(|start| start.try_into().ok())
143 .ok_or(span::Error)?;
144 Ok(())
145 }
146}
147
148/// Syntactic events that occurs in the config.
149#[derive(Clone, Debug)]
150pub(crate) enum Event {
151 /// A comment with a comment tag and the comment itself. Note that the
152 /// comment itself may contain additional whitespace and comment markers
153 /// at the beginning, like `# comment` or `; comment`.
154 Comment(Comment),
155 /// A section header containing the section name and a subsection, if it
156 /// exists. For instance, `remote "origin"` is parsed to `remote` as section
157 /// name and `origin` as subsection name.
158 SectionHeader(section::HeaderData),
159 /// A name to a value in a section, like `url` in `remote.origin.url`.
160 SectionValueName(Span),
161 /// A completed value. This may be any single-line string, including the empty string
162 /// if an implicit boolean value is used.
163 /// Note that these values may contain spaces and any special character. This value is
164 /// also unprocessed, so it may contain double quotes that should be
165 /// [normalized][crate::value::normalize()] before interpretation.
166 Value(Span),
167 /// One or more consecutive line endings.
168 ///
169 /// Both `\n` and `\r\n` are accepted, including mixed runs. Multiple line endings, such as
170 /// `\n\n`, are merged into a single event whose span contains the entire run.
171 Newline(Span),
172 /// Any value that isn't completed. This occurs when the value is continued
173 /// onto the next line by ending it with a backslash.
174 /// A [`Newline`][Self::Newline] event usually follows, followed by either
175 /// `ValueDone`, `Whitespace`, or another `ValueNotDone`. The exception is a
176 /// trailing backslash at EOF, which Git accepts as a continuation and which
177 /// is represented by `ValueNotDone` followed directly by `ValueDone`.
178 ValueNotDone(Span),
179 /// The last line of a value which was continued onto another line.
180 /// With this it's possible to obtain the complete value by concatenating
181 /// the prior [`ValueNotDone`][Self::ValueNotDone] events.
182 ValueDone(Span),
183 /// A continuous section of insignificant whitespace.
184 ///
185 /// Note that values with internal whitespace will not be separated by this event,
186 /// hence interior whitespace there is always part of the value.
187 Whitespace(Span),
188 /// This event is emitted when the parser counters a valid `=` character
189 /// separating the key and value.
190 /// This event is necessary as it eliminates the ambiguity for whitespace
191 /// events between a key and value event.
192 KeyValueSeparator,
193}
194
195/// A view of a syntactic event in a parsed representation.
196///
197/// Values in parsed events can be stored as spans into an owning backing buffer.
198/// This type exposes their resolved byte-string references.
199#[derive(Copy, Clone, Hash, Debug, Eq, PartialEq, PartialOrd, Ord)]
200pub enum EventRef<'a> {
201 /// A comment with a comment tag and the comment itself.
202 Comment {
203 /// The comment marker used.
204 tag: u8,
205 /// The parsed comment text.
206 text: &'a BStr,
207 },
208 /// A section header with its name and optional subsection details.
209 SectionHeader {
210 /// The section name.
211 name: &'a BStr,
212 /// The separator between section and subsection, if any.
213 separator: Option<&'a BStr>,
214 /// The subsection name, if any.
215 subsection_name: Option<&'a BStr>,
216 },
217 /// A name to a value in a section.
218 SectionValueName(&'a BStr),
219 /// A completed value.
220 Value(&'a BStr),
221 /// One or more consecutive line endings.
222 ///
223 /// Both `\n` and `\r\n` are accepted, including mixed runs. Multiple line endings, such as
224 /// `\n\n`, are merged into a single event whose byte string contains the entire run.
225 Newline(&'a BStr),
226 /// An incomplete continued value.
227 ValueNotDone(&'a BStr),
228 /// The final part of a continued value.
229 ValueDone(&'a BStr),
230 /// Insignificant whitespace.
231 Whitespace(&'a BStr),
232 /// A `=` separator between key and value.
233 KeyValueSeparator,
234}
235
236/// A parsed section containing the header and the section events, typically
237/// comprising the keys and their values.
238#[derive(Clone, Debug)]
239pub(crate) struct SectionData {
240 /// The section name and subsection name, if any.
241 pub(crate) header: section::HeaderData,
242 /// The syntactic events found in this section.
243 pub(crate) events: Vec<Event>,
244}
245
246/// A parsed comment containing the comment marker and comment.
247#[derive(Clone, Debug, Default)]
248pub(crate) struct Comment {
249 /// The comment marker used. This is either a semicolon or octothorpe/hash.
250 pub(crate) tag: u8,
251 /// The parsed comment.
252 pub(crate) text: Span,
253}
254
255/// A parser error reports the one-indexed line number where the parsing error
256/// occurred, as well as the last parser node and the remaining data to be
257/// parsed.
258#[derive(PartialEq, Debug)]
259pub struct Error {
260 kind: error::Kind,
261}