add push_section

This commit is contained in:
Edward Shen 2021-03-09 11:04:55 -05:00
parent 801d808ab4
commit f97abaf30d
Signed by: edward
GPG key ID: 19182661E818369F

View file

@ -4,14 +4,11 @@ use crate::parser::{
parse_from_bytes, Error, Event, Key, ParsedSectionHeader, Parser, SectionHeaderName, parse_from_bytes, Error, Event, Key, ParsedSectionHeader, Parser, SectionHeaderName,
}; };
use crate::values::{normalize_bytes, normalize_cow, normalize_vec}; use crate::values::{normalize_bytes, normalize_cow, normalize_vec};
use std::borrow::{Borrow, Cow};
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt::Display; use std::fmt::Display;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut, Range};
use std::{
borrow::{Borrow, Cow},
ops::Range,
};
/// All possible error types that may occur from interacting with [`GitConfig`]. /// All possible error types that may occur from interacting with [`GitConfig`].
#[derive(PartialEq, Eq, Hash, Clone, PartialOrd, Ord, Debug)] #[derive(PartialEq, Eq, Hash, Clone, PartialOrd, Ord, Debug)]
@ -46,7 +43,7 @@ impl std::error::Error for GitConfigError<'_> {}
/// A opaque type that represents a mutable reference to a section. /// A opaque type that represents a mutable reference to a section.
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] #[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct MutableSection<'borrow, 'event> { pub struct MutableSection<'borrow, 'event> {
section: &'borrow mut OwnedSection<'event>, section: &'borrow mut SectionBody<'event>,
implicit_newline: bool, implicit_newline: bool,
whitespace: usize, whitespace: usize,
} }
@ -175,7 +172,7 @@ impl<'borrow, 'event> MutableSection<'borrow, 'event> {
// Internal methods that may require exact indices for faster operations. // Internal methods that may require exact indices for faster operations.
impl<'borrow, 'event> MutableSection<'borrow, 'event> { impl<'borrow, 'event> MutableSection<'borrow, 'event> {
fn new(section: &'borrow mut OwnedSection<'event>) -> Self { fn new(section: &'borrow mut SectionBody<'event>) -> Self {
Self { Self {
section, section,
implicit_newline: true, implicit_newline: true,
@ -225,7 +222,7 @@ impl<'borrow, 'event> MutableSection<'borrow, 'event> {
self.section.0.drain(start..=end); self.section.0.drain(start..=end);
} }
fn set_value(&mut self, index: usize, key: Key<'event>, value: Vec<u8>) { fn set(&mut self, index: usize, key: Key<'event>, value: Vec<u8>) {
self.section self.section
.0 .0
.insert(index, Event::Value(Cow::Owned(value))); .insert(index, Event::Value(Cow::Owned(value)));
@ -235,18 +232,19 @@ impl<'borrow, 'event> MutableSection<'borrow, 'event> {
} }
impl<'event> Deref for MutableSection<'_, 'event> { impl<'event> Deref for MutableSection<'_, 'event> {
type Target = OwnedSection<'event>; type Target = SectionBody<'event>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.section self.section
} }
} }
/// A opaque type that represents a section. /// A opaque type that represents a section body.
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Debug, Default)] #[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Debug, Default)]
pub struct OwnedSection<'event>(Vec<Event<'event>>); pub struct SectionBody<'event>(Vec<Event<'event>>);
impl<'event> OwnedSection<'event> { impl<'event> SectionBody<'event> {
/// Constructs a new empty section body.
fn new() -> Self { fn new() -> Self {
Self::default() Self::default()
} }
@ -416,7 +414,7 @@ impl<'event> OwnedSection<'event> {
} }
} }
impl<'event> From<Vec<Event<'event>>> for OwnedSection<'event> { impl<'event> From<Vec<Event<'event>>> for SectionBody<'event> {
fn from(e: Vec<Event<'event>>) -> Self { fn from(e: Vec<Event<'event>>) -> Self {
Self(e) Self(e)
} }
@ -494,7 +492,7 @@ pub struct GitConfig<'event> {
/// The list of events that occur before an actual section. Since a /// The list of events that occur before an actual section. Since a
/// `git-config` file prohibits global values, this vec is limited to only /// `git-config` file prohibits global values, this vec is limited to only
/// comment, newline, and whitespace events. /// comment, newline, and whitespace events.
frontmatter_events: OwnedSection<'event>, frontmatter_events: SectionBody<'event>,
/// Section name and subsection name to section id lookup tree. This is /// Section name and subsection name to section id lookup tree. This is
/// effectively a n-tree (opposed to a binary tree) that can have a height /// effectively a n-tree (opposed to a binary tree) that can have a height
/// of at most three (including an implicit root node). /// of at most three (including an implicit root node).
@ -504,7 +502,7 @@ pub struct GitConfig<'event> {
/// ///
/// This indirection with the SectionId as the key is critical to flexibly /// This indirection with the SectionId as the key is critical to flexibly
/// supporting `git-config` sections, as duplicated keys are permitted. /// supporting `git-config` sections, as duplicated keys are permitted.
sections: HashMap<SectionId, OwnedSection<'event>>, sections: HashMap<SectionId, SectionBody<'event>>,
section_headers: HashMap<SectionId, ParsedSectionHeader<'event>>, section_headers: HashMap<SectionId, ParsedSectionHeader<'event>>,
/// Internal monotonically increasing counter for section ids. /// Internal monotonically increasing counter for section ids.
section_id_counter: usize, section_id_counter: usize,
@ -640,7 +638,7 @@ impl<'event> GitConfig<'event> {
&mut self, &mut self,
section_name: &'lookup str, section_name: &'lookup str,
subsection_name: Option<&'lookup str>, subsection_name: Option<&'lookup str>,
) -> Result<&OwnedSection<'event>, GitConfigError<'lookup>> { ) -> Result<&SectionBody<'event>, GitConfigError<'lookup>> {
let section_ids = let section_ids =
self.get_section_ids_by_name_and_subname(section_name, subsection_name)?; self.get_section_ids_by_name_and_subname(section_name, subsection_name)?;
Ok(self.sections.get(section_ids.last().unwrap()).unwrap()) Ok(self.sections.get(section_ids.last().unwrap()).unwrap())
@ -698,26 +696,7 @@ impl<'event> GitConfig<'event> {
section_name: impl Into<Cow<'event, str>>, section_name: impl Into<Cow<'event, str>>,
subsection_name: impl Into<Option<Cow<'event, str>>>, subsection_name: impl Into<Option<Cow<'event, str>>>,
) -> MutableSection<'_, 'event> { ) -> MutableSection<'_, 'event> {
let subsection_name = subsection_name.into(); let mut section = self.push_section(section_name, subsection_name, SectionBody::new());
let mut section = if subsection_name.is_some() {
self.push_section(
ParsedSectionHeader {
name: SectionHeaderName(section_name.into()),
separator: Some(" ".into()),
subsection_name,
},
OwnedSection::new(),
)
} else {
self.push_section(
ParsedSectionHeader {
name: SectionHeaderName(section_name.into()),
separator: None,
subsection_name: None,
},
OwnedSection::new(),
)
};
section.push_newline(); section.push_newline();
section section
} }
@ -761,7 +740,7 @@ impl<'event> GitConfig<'event> {
&mut self, &mut self,
section_name: &'lookup str, section_name: &'lookup str,
subsection_name: impl Into<Option<&'lookup str>>, subsection_name: impl Into<Option<&'lookup str>>,
) -> Option<OwnedSection> { ) -> Option<SectionBody> {
let section_ids = let section_ids =
self.get_section_ids_by_name_and_subname(section_name, subsection_name.into()); self.get_section_ids_by_name_and_subname(section_name, subsection_name.into());
let id = section_ids.ok()?.pop()?; let id = section_ids.ok()?.pop()?;
@ -769,6 +748,36 @@ impl<'event> GitConfig<'event> {
.remove(self.section_order.iter().position(|v| *v == id).unwrap()); .remove(self.section_order.iter().position(|v| *v == id).unwrap());
self.sections.remove(&id) self.sections.remove(&id)
} }
/// Adds the provided section to the config, returning a mutable reference
/// to it.
pub fn push_section(
&mut self,
section_name: impl Into<Cow<'event, str>>,
subsection_name: impl Into<Option<Cow<'event, str>>>,
section: SectionBody<'event>,
) -> MutableSection<'_, 'event> {
let subsection_name = subsection_name.into();
if subsection_name.is_some() {
self.push_section_internal(
ParsedSectionHeader {
name: SectionHeaderName(section_name.into()),
separator: Some(" ".into()),
subsection_name,
},
section,
)
} else {
self.push_section_internal(
ParsedSectionHeader {
name: SectionHeaderName(section_name.into()),
separator: None,
subsection_name: None,
},
section,
)
}
}
} }
/// An intermediate representation of a mutable value obtained from /// An intermediate representation of a mutable value obtained from
@ -816,7 +825,7 @@ impl MutableValue<'_, '_, '_> {
} }
self.size = 3; self.size = 3;
self.section self.section
.set_value(self.index, Key(Cow::Owned(self.key.to_string())), input); .set(self.index, Key(Cow::Owned(self.key.to_string())), input);
} }
/// Removes the value. Does nothing when called multiple times in /// Removes the value. Does nothing when called multiple times in
@ -845,7 +854,7 @@ struct EntryData {
/// time. /// time.
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub struct MutableMultiValue<'borrow, 'lookup, 'event> { pub struct MutableMultiValue<'borrow, 'lookup, 'event> {
section: &'borrow mut HashMap<SectionId, OwnedSection<'event>>, section: &'borrow mut HashMap<SectionId, SectionBody<'event>>,
key: Key<'lookup>, key: Key<'lookup>,
/// Each entry data struct provides sufficient information to index into /// Each entry data struct provides sufficient information to index into
/// [`Self::offsets`]. This layer of indirection is used for users to index /// [`Self::offsets`]. This layer of indirection is used for users to index
@ -1039,7 +1048,7 @@ impl<'lookup, 'event> MutableMultiValue<'_, 'lookup, 'event> {
fn set_value_inner<'a: 'event>( fn set_value_inner<'a: 'event>(
key: &Key<'lookup>, key: &Key<'lookup>,
offsets: &mut HashMap<SectionId, Vec<usize>>, offsets: &mut HashMap<SectionId, Vec<usize>>,
section: &mut OwnedSection<'event>, section: &mut SectionBody<'event>,
section_id: SectionId, section_id: SectionId,
offset_index: usize, offset_index: usize,
input: Cow<'a, [u8]>, input: Cow<'a, [u8]>,
@ -1554,12 +1563,12 @@ impl<'event> GitConfig<'event> {
/// Private helper functions /// Private helper functions
impl<'event> GitConfig<'event> { impl<'event> GitConfig<'event> {
/// Adds a new section to the config file. /// Adds a new section to the config file.
fn push_section( fn push_section_internal(
&mut self, &mut self,
// current_section_name: Option<SectionHeaderName<'event>>, // current_section_name: Option<SectionHeaderName<'event>>,
// current_subsection_name: Option<Cow<'event, str>>, // current_subsection_name: Option<Cow<'event, str>>,
header: ParsedSectionHeader<'event>, header: ParsedSectionHeader<'event>,
section: OwnedSection<'event>, section: SectionBody<'event>,
) -> MutableSection<'_, 'event> { ) -> MutableSection<'_, 'event> {
let new_section_id = SectionId(self.section_id_counter); let new_section_id = SectionId(self.section_id_counter);
self.section_headers.insert(new_section_id, header.clone()); self.section_headers.insert(new_section_id, header.clone());
@ -1670,18 +1679,18 @@ impl<'a> From<Parser<'a>> for GitConfig<'a> {
// Current section that we're building // Current section that we're building
let mut prev_section_header = None; let mut prev_section_header = None;
let mut section_events = OwnedSection::new(); let mut section_events = SectionBody::new();
for event in parser.into_iter() { for event in parser.into_iter() {
match event { match event {
Event::SectionHeader(header) => { Event::SectionHeader(header) => {
if let Some(prev_header) = prev_section_header.take() { if let Some(prev_header) = prev_section_header.take() {
new_self.push_section(prev_header, section_events); new_self.push_section_internal(prev_header, section_events);
} else { } else {
new_self.frontmatter_events = section_events; new_self.frontmatter_events = section_events;
} }
prev_section_header = Some(header); prev_section_header = Some(header);
section_events = OwnedSection::new(); section_events = SectionBody::new();
} }
e @ Event::Key(_) e @ Event::Key(_)
| e @ Event::Value(_) | e @ Event::Value(_)
@ -1697,7 +1706,7 @@ impl<'a> From<Parser<'a>> for GitConfig<'a> {
// The last section doesn't get pushed since we only push if there's a // The last section doesn't get pushed since we only push if there's a
// new section header, so we need to call push one more time. // new section header, so we need to call push one more time.
if let Some(header) = prev_section_header { if let Some(header) = prev_section_header {
new_self.push_section(header, section_events); new_self.push_section_internal(header, section_events);
} else { } else {
new_self.frontmatter_events = section_events; new_self.frontmatter_events = section_events;
} }
@ -2052,7 +2061,7 @@ a"#,
#[cfg(test)] #[cfg(test)]
mod from_parser { mod from_parser {
use super::{Cow, Event, GitConfig, HashMap, LookupTreeNode, OwnedSection, SectionId, TryFrom}; use super::{Cow, Event, GitConfig, HashMap, LookupTreeNode, SectionBody, SectionId, TryFrom};
use crate::parser::SectionHeaderName; use crate::parser::SectionHeaderName;
use crate::test_util::{name_event, newline_event, section_header, value_event}; use crate::test_util::{name_event, newline_event, section_header, value_event};
@ -2089,7 +2098,7 @@ mod from_parser {
let mut sections = HashMap::new(); let mut sections = HashMap::new();
sections.insert( sections.insert(
SectionId(0), SectionId(0),
OwnedSection::from(vec![ SectionBody::from(vec![
newline_event(), newline_event(),
name_event("a"), name_event("a"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
@ -2131,7 +2140,7 @@ mod from_parser {
let mut sections = HashMap::new(); let mut sections = HashMap::new();
sections.insert( sections.insert(
SectionId(0), SectionId(0),
OwnedSection::from(vec![ SectionBody::from(vec![
newline_event(), newline_event(),
name_event("a"), name_event("a"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
@ -2176,7 +2185,7 @@ mod from_parser {
let mut sections = HashMap::new(); let mut sections = HashMap::new();
sections.insert( sections.insert(
SectionId(0), SectionId(0),
OwnedSection::from(vec![ SectionBody::from(vec![
newline_event(), newline_event(),
name_event("a"), name_event("a"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
@ -2190,7 +2199,7 @@ mod from_parser {
); );
sections.insert( sections.insert(
SectionId(1), SectionId(1),
OwnedSection::from(vec![ SectionBody::from(vec![
name_event("e"), name_event("e"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
value_event("f"), value_event("f"),
@ -2229,7 +2238,7 @@ mod from_parser {
let mut sections = HashMap::new(); let mut sections = HashMap::new();
sections.insert( sections.insert(
SectionId(0), SectionId(0),
OwnedSection::from(vec![ SectionBody::from(vec![
newline_event(), newline_event(),
name_event("a"), name_event("a"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
@ -2243,7 +2252,7 @@ mod from_parser {
); );
sections.insert( sections.insert(
SectionId(1), SectionId(1),
OwnedSection::from(vec![ SectionBody::from(vec![
name_event("e"), name_event("e"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
value_event("f"), value_event("f"),