Compare commits

..

No commits in common. "65506c733ffff6f07f8a8df62479a35d5aae94d5" and "1cde32efd194aaba6ae6a2263c61c6f56c02b380" have entirely different histories.

2 changed files with 111 additions and 197 deletions

View file

@ -56,22 +56,16 @@ struct SectionId(usize);
/// 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>(&'borrow mut Vec<Event<'event>>);
section: &'borrow mut Vec<Event<'event>>,
implicit_newline: bool,
whitespace: usize,
}
// Immutable methods, effectively a deref into Section. Can't use Deref trait // Immutable methods, effectively a deref into Section
// as there's some lifetime shenanigans that prevent us from matching the trait
// parameters.
impl<'borrow, 'event> MutableSection<'borrow, 'event> { impl<'borrow, 'event> MutableSection<'borrow, 'event> {
/// Retrieves the last matching value in a section with the given key. /// Retrieves the last matching value in a section with the given key.
/// Returns None if the key was not found. /// Returns None if the key was not found.
#[inline] #[inline]
#[must_use] #[must_use]
pub fn value(&self, key: &Key) -> Option<Cow<'event, [u8]>> { pub fn value(&self, key: &Key) -> Option<Cow<'event, [u8]>> {
Section(self.section).value(key) Section(self.0).value(key)
} }
/// Retrieves the last matching value in a section with the given key, and /// Retrieves the last matching value in a section with the given key, and
@ -85,7 +79,7 @@ impl<'borrow, 'event> MutableSection<'borrow, 'event> {
&self, &self,
key: &Key, key: &Key,
) -> Result<T, GitConfigError<'event>> { ) -> Result<T, GitConfigError<'event>> {
Section(self.section).value_as(key) Section(self.0).value_as(key)
} }
/// Retrieves all values that have the provided key name. This may return /// Retrieves all values that have the provided key name. This may return
@ -93,7 +87,7 @@ impl<'borrow, 'event> MutableSection<'borrow, 'event> {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn values(&self, key: &Key) -> Vec<Cow<'event, [u8]>> { pub fn values(&self, key: &Key) -> Vec<Cow<'event, [u8]>> {
Section(self.section).values(key) Section(self.0).values(key)
} }
/// Retrieves all values that have the provided key name. This may return /// Retrieves all values that have the provided key name. This may return
@ -107,24 +101,17 @@ impl<'borrow, 'event> MutableSection<'borrow, 'event> {
&self, &self,
key: &Key, key: &Key,
) -> Result<Vec<T>, GitConfigError<'event>> { ) -> Result<Vec<T>, GitConfigError<'event>> {
Section(self.section).values_as(key) Section(self.0).values_as(key)
} }
} }
// Mutable methods on a mutable section
impl<'borrow, 'event> MutableSection<'borrow, 'event> { impl<'borrow, 'event> MutableSection<'borrow, 'event> {
/// Adds an entry to the end of this section. /// Adds an entry to the end of this section
pub fn push(&mut self, key: Key<'event>, value: Cow<'event, [u8]>) { pub fn push(&mut self, key: Key<'event>, value: Cow<'event, [u8]>) {
if self.whitespace > 0 { self.0.push(Event::Key(key));
self.section self.0.push(Event::KeyValueSeparator);
.push(Event::Whitespace(" ".repeat(self.whitespace).into())); self.0.push(Event::Value(value));
}
self.section.push(Event::Key(key));
self.section.push(Event::KeyValueSeparator);
self.section.push(Event::Value(value));
if self.implicit_newline {
self.section.push(Event::Newline("\n".into()));
}
} }
/// Removes all events until a key value pair is removed. This will also /// Removes all events until a key value pair is removed. This will also
@ -132,12 +119,12 @@ impl<'borrow, 'event> MutableSection<'borrow, 'event> {
pub fn pop(&mut self) -> Option<(Key, Cow<'event, [u8]>)> { pub fn pop(&mut self) -> Option<(Key, Cow<'event, [u8]>)> {
let mut values = vec![]; let mut values = vec![];
// events are popped in reverse order // events are popped in reverse order
while let Some(e) = self.section.pop() { while let Some(e) = self.0.pop() {
match e { match e {
Event::Key(k) => { Event::Key(k) => {
// pop leading whitespace // pop leading whitespace
if let Some(Event::Whitespace(_)) = self.section.last() { if let Some(Event::Whitespace(_)) = self.0.last() {
self.section.pop(); self.0.pop();
} }
if values.len() == 1 { if values.len() == 1 {
@ -160,43 +147,10 @@ impl<'borrow, 'event> MutableSection<'borrow, 'event> {
} }
None None
} }
/// Adds a new line event. Note that you don't need to call this unless
/// you've disabled implicit newlines.
pub fn push_newline(&mut self) {
self.section.push(Event::Newline("\n".into()));
}
/// Enables or disables automatically adding newline events after adding
/// a value. This is enabled by default.
pub fn implicit_newline(&mut self, on: bool) {
self.implicit_newline = on;
}
/// Sets the number of spaces before the start of a key value. By default,
/// this is set to two. Set to 0 to disable adding whitespace before a key
/// value.
pub fn set_whitespace(&mut self, num: usize) {
self.whitespace = num;
}
/// Returns the number of whitespace this section will insert before the
/// beginning of a key.
pub const fn whitespace(&self) -> usize {
self.whitespace
}
} }
// Internal methods that may require exact indices for faster operations. // Internal methods that require exact indices for faster operations.
impl<'borrow, 'event> MutableSection<'borrow, 'event> { impl<'borrow, 'event> MutableSection<'borrow, 'event> {
fn new(section: &'borrow mut Vec<Event<'event>>) -> Self {
Self {
section,
implicit_newline: true,
whitespace: 2,
}
}
fn get<'key>( fn get<'key>(
&self, &self,
key: &Key<'key>, key: &Key<'key>,
@ -209,7 +163,7 @@ impl<'borrow, 'event> MutableSection<'borrow, 'event> {
// section_id is guaranteed to exist in self.sections, else we have a // section_id is guaranteed to exist in self.sections, else we have a
// violated invariant. // violated invariant.
for event in &self.section[start..=end] { for event in &self.0[start..=end] {
match event { match event {
Event::Key(event_key) if event_key == key => found_key = true, Event::Key(event_key) if event_key == key => found_key = true,
Event::Value(v) if found_key => { Event::Value(v) if found_key => {
@ -236,13 +190,13 @@ impl<'borrow, 'event> MutableSection<'borrow, 'event> {
} }
fn delete(&mut self, start: usize, end: usize) { fn delete(&mut self, start: usize, end: usize) {
self.section.drain(start..=end); self.0.drain(start..=end);
} }
fn set_value(&mut self, index: usize, key: Key<'event>, value: Vec<u8>) { fn set_value(&mut self, index: usize, key: Key<'event>, value: Vec<u8>) {
self.section.insert(index, Event::Value(Cow::Owned(value))); self.0.insert(index, Event::Value(Cow::Owned(value)));
self.section.insert(index, Event::KeyValueSeparator); self.0.insert(index, Event::KeyValueSeparator);
self.section.insert(index, Event::Key(key)); self.0.insert(index, Event::Key(key));
} }
} }
@ -792,7 +746,7 @@ impl<'lookup, 'event> MutableMultiValue<'_, 'lookup, 'event> {
/// with all values instead. /// with all values instead.
/// ///
/// [`get_raw_value`]: Self::get_raw_value /// [`get_raw_value`]: Self::get_raw_value
#[derive(PartialEq, Eq, Clone, Debug, Default)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct GitConfig<'a> { pub struct GitConfig<'a> {
/// 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
@ -811,12 +765,6 @@ pub struct GitConfig<'a> {
} }
impl<'event> GitConfig<'event> { impl<'event> GitConfig<'event> {
/// Constructs an empty `git-config` file.
#[must_use]
pub fn new() -> Self {
Self::default()
}
/// Returns an interpreted value given a section, an optional subsection and /// Returns an interpreted value given a section, an optional subsection and
/// key. /// key.
/// ///
@ -839,12 +787,12 @@ impl<'event> GitConfig<'event> {
/// a = 10k /// a = 10k
/// c /// c
/// "#; /// "#;
/// let git_config = GitConfig::try_from(config)?; /// let git_config = GitConfig::try_from(config).unwrap();
/// // You can either use the turbofish to determine the type... /// // You can either use the turbofish to determine the type...
/// let a_value = git_config.value::<Integer>("core", None, "a")?; /// let a_value = git_config.value::<Integer>("core", None, "a")?;
/// // ... or explicitly declare the type to avoid the turbofish /// // ... or explicitly declare the type to avoid the turbofish
/// let c_value: Boolean = git_config.value("core", None, "c")?; /// let c_value: Boolean = git_config.value("core", None, "c")?;
/// # Ok::<(), Box<dyn std::error::Error>>(()) /// # Ok::<(), GitConfigError>(())
/// ``` /// ```
/// ///
/// # Errors /// # Errors
@ -929,11 +877,6 @@ impl<'event> GitConfig<'event> {
} }
/// Returns an immutable section reference. /// Returns an immutable section reference.
///
/// # Errors
///
/// This function will return an error if the section and optional
/// subsection do not exist.
pub fn section<'lookup>( pub fn section<'lookup>(
&mut self, &mut self,
section_name: &'lookup str, section_name: &'lookup str,
@ -947,11 +890,6 @@ impl<'event> GitConfig<'event> {
} }
/// Returns an mutable section reference. /// Returns an mutable section reference.
///
/// # Errors
///
/// This function will return an error if the section and optional
/// subsection do not exist.
pub fn section_mut<'lookup>( pub fn section_mut<'lookup>(
&mut self, &mut self,
section_name: &'lookup str, section_name: &'lookup str,
@ -960,66 +898,23 @@ impl<'event> GitConfig<'event> {
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(MutableSection::new( Ok(MutableSection(
self.sections.get_mut(section_ids.last().unwrap()).unwrap(), self.sections.get_mut(section_ids.last().unwrap()).unwrap(),
)) ))
} }
/// Adds a new section to config. If a subsection name was provided, then /// Adds a new section to config. This cannot fail.
/// the generated header will use the modern subsection syntax. Returns a
/// reference to the new section for immediate editing.
///
/// # Examples
///
/// Creating a new empty section:
///
/// ```
/// # use git_config::file::{GitConfig, GitConfigError};
/// # use std::convert::TryFrom;
/// let mut git_config = GitConfig::new();
/// let _section = git_config.new_section("hello", Some("world".into()));
/// assert_eq!(git_config.to_string(), "[hello \"world\"]\n");
/// ```
///
/// Creating a new empty section and adding values to it:
///
/// ```
/// # use git_config::file::{GitConfig, GitConfigError};
/// # use std::convert::TryFrom;
/// let mut git_config = GitConfig::new();
/// let mut section = git_config.new_section("hello", Some("world".into()));
/// section.push("a".into(), "b".as_bytes().into());
/// assert_eq!(git_config.to_string(), "[hello \"world\"]\n a=b\n");
/// let _section = git_config.new_section("core", None);
/// assert_eq!(git_config.to_string(), "[hello \"world\"]\n a=b\n[core]\n");
/// ```
pub fn new_section( pub fn new_section(
&mut self, &mut self,
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 = if subsection_name.is_some() {
self.push_section( self.push_section(
ParsedSectionHeader { Some(SectionHeaderName(section_name.into())),
name: SectionHeaderName(section_name.into()), subsection_name.into(),
separator: Some(" ".into()), &mut Some(vec![]),
subsection_name,
},
vec![],
) )
} else { .unwrap()
self.push_section(
ParsedSectionHeader {
name: SectionHeaderName(section_name.into()),
separator: None,
subsection_name: None,
},
vec![],
)
};
section.push_newline();
section
} }
/// Removes the section, returning the events it had, if any. If multiple /// Removes the section, returning the events it had, if any. If multiple
@ -1125,7 +1020,7 @@ impl<'event> GitConfig<'event> {
} }
return Ok(MutableValue { return Ok(MutableValue {
section: MutableSection::new(self.sections.get_mut(section_id).unwrap()), section: MutableSection(self.sections.get_mut(section_id).unwrap()),
key, key,
size, size,
index, index,
@ -1454,18 +1349,20 @@ 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(
&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>, maybe_section: &mut Option<Vec<Event<'event>>>,
maybe_section: Vec<Event<'event>>, ) -> Option<MutableSection<'_, 'event>> {
) -> MutableSection<'_, 'event> { if let Some(section) = maybe_section.take() {
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.sections.insert(new_section_id, section);
self.sections.insert(new_section_id, maybe_section); let lookup = self
let lookup = self.section_lookup_tree.entry(header.name).or_default(); .section_lookup_tree
.entry(current_section_name.unwrap())
.or_default();
let mut found_node = false; let mut found_node = false;
if let Some(subsection_name) = header.subsection_name { if let Some(subsection_name) = current_subsection_name {
for node in lookup.iter_mut() { for node in lookup.iter_mut() {
if let LookupTreeNode::NonTerminal(subsection) = node { if let LookupTreeNode::NonTerminal(subsection) = node {
found_node = true; found_node = true;
@ -1496,10 +1393,10 @@ impl<'event> GitConfig<'event> {
} }
self.section_order.push_back(new_section_id); self.section_order.push_back(new_section_id);
self.section_id_counter += 1; self.section_id_counter += 1;
self.sections self.sections.get_mut(&new_section_id).map(MutableSection)
.get_mut(&new_section_id) } else {
.map(MutableSection::new) None
.unwrap() }
} }
/// Returns the mapping between section and subsection name to section ids. /// Returns the mapping between section and subsection name to section ids.
@ -1574,40 +1471,57 @@ 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 current_section_name: Option<SectionHeaderName<'_>> = None;
let mut section_events: Vec<Event<'a>> = vec![]; let mut current_subsection_name: Option<Cow<'a, str>> = None;
let mut maybe_section: Option<Vec<Event<'a>>> = None;
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() { new_self.push_section(
new_self.push_section(prev_header, section_events); current_section_name,
} else { current_subsection_name,
new_self.frontmatter_events = section_events; &mut maybe_section,
} );
prev_section_header = Some(header);
section_events = vec![]; // Initialize new section
// We need to store the new, current id counter, so don't
// use new_section_id here and use the already incremented
// section id value.
new_self
.section_headers
.insert(SectionId(new_self.section_id_counter), header.clone());
let (name, subname) = (header.name, header.subsection_name);
maybe_section = Some(vec![]);
current_section_name = Some(name);
current_subsection_name = subname;
} }
e @ Event::Key(_) e @ Event::Key(_)
| e @ Event::Value(_) | e @ Event::Value(_)
| e @ Event::ValueNotDone(_) | e @ Event::ValueNotDone(_)
| e @ Event::ValueDone(_) | e @ Event::ValueDone(_)
| e @ Event::KeyValueSeparator => section_events.push(e), | e @ Event::KeyValueSeparator => maybe_section
.as_mut()
.expect("Got a section-only event before a section")
.push(e),
e @ Event::Comment(_) | e @ Event::Newline(_) | e @ Event::Whitespace(_) => { e @ Event::Comment(_) | e @ Event::Newline(_) | e @ Event::Whitespace(_) => {
section_events.push(e); match maybe_section {
Some(ref mut section) => section.push(e),
None => new_self.frontmatter_events.push(e),
}
} }
} }
} }
// 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 { new_self.push_section(
new_self.push_section(header, section_events); current_section_name,
} else { current_subsection_name,
new_self.frontmatter_events = section_events; &mut maybe_section,
} );
dbg!(new_self) new_self
} }
} }
@ -1851,6 +1765,7 @@ mod mutable_multi_value {
.get_raw_multi_value_mut("core", None, "a") .get_raw_multi_value_mut("core", None, "a")
.unwrap(); .unwrap();
values.set_string(0, "Hello".to_string()); values.set_string(0, "Hello".to_string());
dbg!(values);
assert_eq!( assert_eq!(
git_config.to_string(), git_config.to_string(),
r#"[core] r#"[core]
@ -2159,7 +2074,7 @@ mod from_parser {
#[cfg(test)] #[cfg(test)]
mod get_raw_value { mod get_raw_value {
use super::{Cow, GitConfig, GitConfigError, TryFrom}; use super::{Cow, GitConfig, GitConfigError, TryFrom};
use crate::parser::SectionHeaderName; use crate::parser::{Key, SectionHeaderName};
#[test] #[test]
fn single_section() { fn single_section() {

View file

@ -222,7 +222,6 @@ generate_case_insensitive!(
Cow<'a, str>, Cow<'a, str>,
"Wrapper struct for section header names, since section headers are case-insensitive." "Wrapper struct for section header names, since section headers are case-insensitive."
); );
generate_case_insensitive!( generate_case_insensitive!(
Key, Key,
Cow<'a, str>, Cow<'a, str>,