//! This module handles parsing a `git-config` file. Generally speaking, you //! want to use a higher abstraction such as [`File`] unless you have some //! explicit reason to work with events instead. //! //! Use [`Events::from_bytes()`] to obtain a self-contained parsed representation, //! then iterate over its event views. //! //! On a higher level, one can use [`Events`] to parse all events into a set //! of easily interpretable data type, similar to what [`File`] does. //! //! [`File`]: crate::File use bstr::{BStr, BString, ByteSlice}; mod from_bytes; mod event; #[path = "events.rs"] mod events_type; pub(crate) use events_type::FrontMatterEvents; pub use events_type::{Events, SectionRef}; mod comment; mod error; /// pub mod section; #[cfg(test)] pub(crate) mod tests; /// A range into a shared backing buffer. #[derive(Copy, Clone, Debug, Default)] pub(crate) struct Span { start: u32, len: u32, } /// Errors produced when a span cannot be represented. pub mod span { /// A span offset or length exceeded the supported 32-bit representation. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, thiserror::Error)] #[error("configuration data exceeds the supported span size of {} bytes", u32::MAX)] pub struct Error; } /// A raw span whose semantic value may have required decoding while parsing. /// /// The raw bytes are retained for lossless serialization. `decoded` is present only when those /// bytes had to be transformed for semantic access, such as an escaped quoted subsection name. #[derive(Clone, Debug)] pub(crate) struct MaybeDecoded { raw: Span, decoded: Option, } impl MaybeDecoded { pub(crate) fn raw(raw: Span) -> Self { Self { raw, decoded: None } } pub(crate) fn decoded(raw: Span, decoded: BString) -> Self { Self { raw, decoded: Some(decoded), } } pub(crate) fn raw_span(&self) -> Span { self.raw } pub(crate) fn value_in<'a>(&'a self, backing: &'a [u8]) -> &'a BStr { self.decoded .as_ref() .map_or_else(|| self.raw.as_bstr_in(backing), |value| value.as_bstr()) } pub(crate) fn rebase(&mut self, offset: usize) -> Result<(), span::Error> { self.raw.rebase(offset) } pub(crate) fn copy_to_backing_in(&self, source: &[u8], target: &mut Vec) -> Result { Ok(Self { raw: self.raw.copy_to_backing_in(source, target)?, decoded: self.decoded.clone(), }) } } impl Span { pub(crate) fn append(backing: &mut Vec, bytes: &[u8]) -> Result { let start = backing.len(); let span = Self::range(start, bytes.len())?; backing.len().checked_add(bytes.len()).ok_or(span::Error)?; backing.extend_from_slice(bytes); Ok(span) } pub(crate) fn range(start: usize, len: usize) -> Result { Ok(Span { start: start.try_into().map_err(|_| span::Error)?, len: len.try_into().map_err(|_| span::Error)?, }) } pub(crate) fn new(backing: &[u8], bytes: &[u8]) -> Self { if bytes.is_empty() { return Self::default(); } debug_assert!(!backing.is_empty()); let base = backing.as_ptr() as usize; let start = (bytes.as_ptr() as usize) .checked_sub(base) .expect("span must point into the backing buffer"); let end = start + bytes.len(); debug_assert!(end <= backing.len()); Self::range(start, bytes.len()).expect("the parser rejects backing buffers that exceed the span limit") } /// Return ourselves as byte string slice using `backing` to resolve spans. pub fn as_bstr_in<'a>(&'a self, backing: &'a [u8]) -> &'a BStr { self.as_slice_in(backing).as_bstr() } /// Return ourselves as bytes using `backing` to resolve spans. pub fn as_slice_in<'a>(&'a self, backing: &'a [u8]) -> &'a [u8] { let start = self.start as usize; &backing[start..start + self.len as usize] } /// Convert into owned bytes using `backing` to resolve spans. pub fn to_bstring_in(self, backing: &[u8]) -> BString { self.as_slice_in(backing).into() } pub(crate) fn copy_to_backing_in(&self, source: &[u8], target: &mut Vec) -> Result { Span::append(target, self.as_slice_in(source)) } pub(crate) fn rebase(&mut self, offset: usize) -> Result<(), span::Error> { self.start = (self.start as usize) .checked_add(offset) .and_then(|start| start.try_into().ok()) .ok_or(span::Error)?; Ok(()) } } /// Syntactic events that occurs in the config. #[derive(Clone, Debug)] pub(crate) enum Event { /// A comment with a comment tag and the comment itself. Note that the /// comment itself may contain additional whitespace and comment markers /// at the beginning, like `# comment` or `; comment`. Comment(Comment), /// A section header containing the section name and a subsection, if it /// exists. For instance, `remote "origin"` is parsed to `remote` as section /// name and `origin` as subsection name. SectionHeader(section::HeaderData), /// A name to a value in a section, like `url` in `remote.origin.url`. SectionValueName(Span), /// A completed value. This may be any single-line string, including the empty string /// if an implicit boolean value is used. /// Note that these values may contain spaces and any special character. This value is /// also unprocessed, so it may contain double quotes that should be /// [normalized][crate::value::normalize()] before interpretation. Value(Span), /// One or more consecutive line endings. /// /// Both `\n` and `\r\n` are accepted, including mixed runs. Multiple line endings, such as /// `\n\n`, are merged into a single event whose span contains the entire run. Newline(Span), /// Any value that isn't completed. This occurs when the value is continued /// onto the next line by ending it with a backslash. /// A [`Newline`][Self::Newline] event usually follows, followed by either /// `ValueDone`, `Whitespace`, or another `ValueNotDone`. The exception is a /// trailing backslash at EOF, which Git accepts as a continuation and which /// is represented by `ValueNotDone` followed directly by `ValueDone`. ValueNotDone(Span), /// The last line of a value which was continued onto another line. /// With this it's possible to obtain the complete value by concatenating /// the prior [`ValueNotDone`][Self::ValueNotDone] events. ValueDone(Span), /// A continuous section of insignificant whitespace. /// /// Note that values with internal whitespace will not be separated by this event, /// hence interior whitespace there is always part of the value. Whitespace(Span), /// This event is emitted when the parser counters a valid `=` character /// separating the key and value. /// This event is necessary as it eliminates the ambiguity for whitespace /// events between a key and value event. KeyValueSeparator, } /// A view of a syntactic event in a parsed representation. /// /// Values in parsed events can be stored as spans into an owning backing buffer. /// This type exposes their resolved byte-string references. #[derive(Copy, Clone, Hash, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum EventRef<'a> { /// A comment with a comment tag and the comment itself. Comment { /// The comment marker used. tag: u8, /// The parsed comment text. text: &'a BStr, }, /// A section header with its name and optional subsection details. SectionHeader { /// The section name. name: &'a BStr, /// The separator between section and subsection, if any. separator: Option<&'a BStr>, /// The subsection name, if any. subsection_name: Option<&'a BStr>, }, /// A name to a value in a section. SectionValueName(&'a BStr), /// A completed value. Value(&'a BStr), /// One or more consecutive line endings. /// /// Both `\n` and `\r\n` are accepted, including mixed runs. Multiple line endings, such as /// `\n\n`, are merged into a single event whose byte string contains the entire run. Newline(&'a BStr), /// An incomplete continued value. ValueNotDone(&'a BStr), /// The final part of a continued value. ValueDone(&'a BStr), /// Insignificant whitespace. Whitespace(&'a BStr), /// A `=` separator between key and value. KeyValueSeparator, } /// A parsed section containing the header and the section events, typically /// comprising the keys and their values. #[derive(Clone, Debug)] pub(crate) struct SectionData { /// The section name and subsection name, if any. pub(crate) header: section::HeaderData, /// The syntactic events found in this section. pub(crate) events: Vec, } /// A parsed comment containing the comment marker and comment. #[derive(Clone, Debug, Default)] pub(crate) struct Comment { /// The comment marker used. This is either a semicolon or octothorpe/hash. pub(crate) tag: u8, /// The parsed comment. pub(crate) text: Span, } /// A parser error reports the one-indexed line number where the parsing error /// occurred, as well as the last parser node and the remaining data to be /// parsed. #[derive(PartialEq, Debug)] pub struct Error { kind: error::Kind, }