Byronreview1825e853 months agoHistory
261 lines · 9.4 KB · Rust
RawDownload
//! 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<BString>,} 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<u8>) -> Result<Self, span::Error> {        Ok(Self {            raw: self.raw.copy_to_backing_in(source, target)?,            decoded: self.decoded.clone(),        })    }} impl Span {    pub(crate) fn append(backing: &mut Vec<u8>, bytes: &[u8]) -> Result<Self, span::Error> {        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<Self, span::Error> {        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<u8>) -> Result<Self, span::Error> {        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<Event>,} /// 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,}