Skip to main content

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}