Use BStr instead

This commit is contained in:
Edward Shen 2021-02-23 19:47:24 -05:00
parent f82d32953e
commit ea0f76d528
Signed by: edward
GPG key ID: 19182661E818369F
7 changed files with 552 additions and 495 deletions

12
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,12 @@
{
"cSpell.words": [
"Multivar",
"autocrlf",
"bstr",
"gitea",
"gpgsign",
"implicits",
"multivars",
"subname"
]
}

View file

@ -9,6 +9,7 @@ edition = "2018"
[dependencies] [dependencies]
serde = "1.0" serde = "1.0"
nom = "6" nom = "6"
bstr = "0.2.15"
[dev-dependencies] [dev-dependencies]
serde_derive = "1.0" serde_derive = "1.0"

View file

@ -1,16 +1,16 @@
use crate::parser::{parse_from_str, Event, ParsedSectionHeader, Parser, ParserError}; use crate::parser::{parse_from_str, Event, ParsedSectionHeader, Parser, ParserError};
use serde::{ser::SerializeMap, Serialize, Serializer}; use bstr::BStr;
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
use std::{borrow::Cow, fmt::Display}; use std::{borrow::Cow, fmt::Display};
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum GitConfigError<'a> { pub enum GitConfigError<'a> {
/// The requested section does not exist. /// The requested section does not exist.
SectionDoesNotExist(&'a str), SectionDoesNotExist(&'a BStr),
/// The requested subsection does not exist. /// The requested subsection does not exist.
SubSectionDoesNotExist(Option<&'a str>), SubSectionDoesNotExist(Option<&'a BStr>),
/// The key does not exist in the requested section. /// The key does not exist in the requested section.
KeyDoesNotExist(&'a str), KeyDoesNotExist(&'a BStr),
} }
/// High level `git-config` reader and writer. /// High level `git-config` reader and writer.
@ -19,7 +19,7 @@ pub struct GitConfig<'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.
front_matter_events: Vec<Event<'a>>, front_matter_events: Vec<Event<'a>>,
section_lookup_tree: HashMap<Cow<'a, str>, Vec<LookupTreeNode<'a>>>, section_lookup_tree: HashMap<Cow<'a, BStr>, Vec<LookupTreeNode<'a>>>,
/// SectionId to section mapping. The value of this HashMap contains actual /// SectionId to section mapping. The value of this HashMap contains actual
/// events /// events
sections: HashMap<SectionId, Vec<Event<'a>>>, sections: HashMap<SectionId, Vec<Event<'a>>>,
@ -45,7 +45,7 @@ struct SectionId(usize);
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
enum LookupTreeNode<'a> { enum LookupTreeNode<'a> {
Terminal(Vec<SectionId>), Terminal(Vec<SectionId>),
NonTerminal(HashMap<Cow<'a, str>, Vec<SectionId>>), NonTerminal(HashMap<Cow<'a, BStr>, Vec<SectionId>>),
} }
impl<'a> GitConfig<'a> { impl<'a> GitConfig<'a> {
@ -68,8 +68,8 @@ impl<'a> GitConfig<'a> {
}; };
// Current section that we're building // Current section that we're building
let mut current_section_name: Option<Cow<'a, str>> = None; let mut current_section_name: Option<Cow<'a, BStr>> = None;
let mut current_subsection_name: Option<Cow<'a, str>> = None; let mut current_subsection_name: Option<Cow<'a, BStr>> = None;
let mut maybe_section: Option<Vec<Event<'a>>> = None; let mut maybe_section: Option<Vec<Event<'a>>> = None;
for event in parser.into_iter() { for event in parser.into_iter() {
@ -123,8 +123,8 @@ impl<'a> GitConfig<'a> {
fn push_section( fn push_section(
&mut self, &mut self,
current_section_name: Option<Cow<'a, str>>, current_section_name: Option<Cow<'a, BStr>>,
current_subsection_name: Option<Cow<'a, str>>, current_subsection_name: Option<Cow<'a, BStr>>,
maybe_section: &mut Option<Vec<Event<'a>>>, maybe_section: &mut Option<Vec<Event<'a>>>,
) { ) {
if let Some(section) = maybe_section.take() { if let Some(section) = maybe_section.take() {
@ -203,7 +203,7 @@ impl<'a> GitConfig<'a> {
/// # use serde_git_config::config::GitConfig; /// # use serde_git_config::config::GitConfig;
/// # use std::borrow::Cow; /// # use std::borrow::Cow;
/// # let git_config = GitConfig::from_str("[core]a=b\n[core]\na=c\na=d").unwrap(); /// # let git_config = GitConfig::from_str("[core]a=b\n[core]\na=c\na=d").unwrap();
/// assert_eq!(git_config.get_raw_value("core", None, "a"), Ok(&Cow::from("d"))); /// assert_eq!(git_config.get_raw_value("core", None, "a"), Ok(&Cow::Borrowed("d".into())));
/// ``` /// ```
/// ///
/// Consider [`Self::get_raw_multi_value`] if you want to get all values of /// Consider [`Self::get_raw_multi_value`] if you want to get all values of
@ -213,16 +213,20 @@ impl<'a> GitConfig<'a> {
/// ///
/// This function will return an error if the key is not in the requested /// This function will return an error if the key is not in the requested
/// section and subsection, or if the section and subsection do not exist. /// section and subsection, or if the section and subsection do not exist.
pub fn get_raw_value<'b>( pub fn get_raw_value<'b, S: Into<&'b BStr>>(
&self, &self,
section_name: &'b str, section_name: S,
subsection_name: Option<&'b str>, subsection_name: Option<S>,
key: &'b str, key: S,
) -> Result<&Cow<'a, str>, GitConfigError<'b>> { ) -> Result<&Cow<'a, BStr>, GitConfigError<'b>> {
let key = key.into();
// Note: cannot wrap around the raw_multi_value method because we need // Note: cannot wrap around the raw_multi_value method because we need
// to guarantee that the highest section id is used (so that we follow // to guarantee that the highest section id is used (so that we follow
// the "last one wins" resolution strategy by `git-config`). // the "last one wins" resolution strategy by `git-config`).
let section_id = self.get_section_id_by_name_and_subname(section_name, subsection_name)?; let section_id = self.get_section_id_by_name_and_subname(
section_name.into(),
subsection_name.map(Into::into),
)?;
// 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.
@ -273,11 +277,12 @@ impl<'a> GitConfig<'a> {
/// ``` /// ```
/// # use serde_git_config::config::{GitConfig, GitConfigError}; /// # use serde_git_config::config::{GitConfig, GitConfigError};
/// # use std::borrow::Cow; /// # use std::borrow::Cow;
/// # use bstr::BStr;
/// # let mut git_config = GitConfig::from_str("[core]a=b\n[core]\na=c\na=d").unwrap(); /// # let mut git_config = GitConfig::from_str("[core]a=b\n[core]\na=c\na=d").unwrap();
/// let mut mut_value = git_config.get_raw_value_mut("core", None, "a")?; /// let mut mut_value = git_config.get_raw_value_mut("core", None, "a")?;
/// assert_eq!(mut_value, &mut Cow::from("d")); /// assert_eq!(mut_value, &mut Cow::<BStr>::Borrowed("d".into()));
/// *mut_value = Cow::Borrowed("hello"); /// *mut_value = Cow::Borrowed("hello".into());
/// assert_eq!(git_config.get_raw_value("core", None, "a"), Ok(&Cow::from("hello"))); /// assert_eq!(git_config.get_raw_value("core", None, "a"), Ok(&Cow::Borrowed("hello".into())));
/// # Ok::<(), GitConfigError>(()) /// # Ok::<(), GitConfigError>(())
/// ``` /// ```
/// ///
@ -288,16 +293,20 @@ impl<'a> GitConfig<'a> {
/// ///
/// This function will return an error if the key is not in the requested /// This function will return an error if the key is not in the requested
/// section and subsection, or if the section and subsection do not exist. /// section and subsection, or if the section and subsection do not exist.
pub fn get_raw_value_mut<'b>( pub fn get_raw_value_mut<'b, S: Into<&'b BStr>>(
&mut self, &mut self,
section_name: &'b str, section_name: S,
subsection_name: Option<&'b str>, subsection_name: Option<S>,
key: &'b str, key: S,
) -> Result<&mut Cow<'a, str>, GitConfigError<'b>> { ) -> Result<&mut Cow<'a, BStr>, GitConfigError<'b>> {
let key = key.into();
// Note: cannot wrap around the raw_multi_value method because we need // Note: cannot wrap around the raw_multi_value method because we need
// to guarantee that the highest section id is used (so that we follow // to guarantee that the highest section id is used (so that we follow
// the "last one wins" resolution strategy by `git-config`). // the "last one wins" resolution strategy by `git-config`).
let section_id = self.get_section_id_by_name_and_subname(section_name, subsection_name)?; let section_id = self.get_section_id_by_name_and_subname(
section_name.into(),
subsection_name.map(Into::into),
)?;
// 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.
@ -320,8 +329,8 @@ impl<'a> GitConfig<'a> {
fn get_section_id_by_name_and_subname<'b>( fn get_section_id_by_name_and_subname<'b>(
&'a self, &'a self,
section_name: &'b str, section_name: &'b BStr,
subsection_name: Option<&'b str>, subsection_name: Option<&'b BStr>,
) -> Result<SectionId, GitConfigError<'b>> { ) -> Result<SectionId, GitConfigError<'b>> {
self.get_section_ids_by_name_and_subname(section_name, subsection_name) self.get_section_ids_by_name_and_subname(section_name, subsection_name)
.map(|vec| { .map(|vec| {
@ -350,7 +359,7 @@ impl<'a> GitConfig<'a> {
/// # let git_config = GitConfig::from_str("[core]a=b\n[core]\na=c\na=d").unwrap(); /// # let git_config = GitConfig::from_str("[core]a=b\n[core]\na=c\na=d").unwrap();
/// assert_eq!( /// assert_eq!(
/// git_config.get_raw_multi_value("core", None, "a"), /// git_config.get_raw_multi_value("core", None, "a"),
/// Ok(vec![&Cow::from("b"), &Cow::from("c"), &Cow::from("d")]), /// Ok(vec![&Cow::Borrowed("b".into()), &Cow::Borrowed("c".into()), &Cow::Borrowed("d".into())]),
/// ); /// );
/// ``` /// ```
/// ///
@ -362,14 +371,18 @@ impl<'a> GitConfig<'a> {
/// This function will return an error if the key is not in any requested /// This function will return an error if the key is not in any requested
/// section and subsection, or if no instance of the section and subsections /// section and subsection, or if no instance of the section and subsections
/// exist. /// exist.
pub fn get_raw_multi_value<'b>( pub fn get_raw_multi_value<'b, S: Into<&'b BStr>>(
&'a self, &'a self,
section_name: &'b str, section_name: S,
subsection_name: Option<&'b str>, subsection_name: Option<S>,
key: &'b str, key: S,
) -> Result<Vec<&Cow<'a, str>>, GitConfigError<'b>> { ) -> Result<Vec<&Cow<'a, BStr>>, GitConfigError<'b>> {
let key = key.into();
let mut values = vec![]; let mut values = vec![];
for section_id in self.get_section_ids_by_name_and_subname(section_name, subsection_name)? { for section_id in self.get_section_ids_by_name_and_subname(
section_name.into(),
subsection_name.map(Into::into),
)? {
let mut found_key = false; let mut found_key = false;
// section_id is guaranteed to exist in self.sections, else we // section_id is guaranteed to exist in self.sections, else we
// have a violated invariant. // have a violated invariant.
@ -408,14 +421,26 @@ impl<'a> GitConfig<'a> {
/// ``` /// ```
/// # use serde_git_config::config::{GitConfig, GitConfigError}; /// # use serde_git_config::config::{GitConfig, GitConfigError};
/// # use std::borrow::Cow; /// # use std::borrow::Cow;
/// # use bstr::BStr;
/// # let mut git_config = GitConfig::from_str("[core]a=b\n[core]\na=c\na=d").unwrap(); /// # let mut git_config = GitConfig::from_str("[core]a=b\n[core]\na=c\na=d").unwrap();
/// assert_eq!(git_config.get_raw_multi_value("core", None, "a")?, vec!["b", "c", "d"]); /// assert_eq!(
/// git_config.get_raw_multi_value("core", None, "a")?,
/// vec![
/// &Cow::<BStr>::Borrowed("b".into()),
/// &Cow::<BStr>::Borrowed("c".into()),
/// &Cow::<BStr>::Borrowed("d".into())
/// ]
/// );
/// for value in git_config.get_raw_multi_value_mut("core", None, "a")? { /// for value in git_config.get_raw_multi_value_mut("core", None, "a")? {
/// *value = Cow::from("g"); /// *value = Cow::Borrowed("g".into());
///} ///}
/// assert_eq!( /// assert_eq!(
/// git_config.get_raw_multi_value("core", None, "a")?, /// git_config.get_raw_multi_value("core", None, "a")?,
/// vec![&Cow::from("g"), &Cow::from("g"), &Cow::from("g")], /// vec![
/// &Cow::<BStr>::Borrowed("g".into()),
/// &Cow::<BStr>::Borrowed("g".into()),
/// &Cow::<BStr>::Borrowed("g".into())
/// ],
/// ); /// );
/// # Ok::<(), GitConfigError>(()) /// # Ok::<(), GitConfigError>(())
/// ``` /// ```
@ -431,17 +456,21 @@ impl<'a> GitConfig<'a> {
/// This function will return an error if the key is not in any requested /// This function will return an error if the key is not in any requested
/// section and subsection, or if no instance of the section and subsections /// section and subsection, or if no instance of the section and subsections
/// exist. /// exist.
pub fn get_raw_multi_value_mut<'b>( pub fn get_raw_multi_value_mut<'b, S: Into<&'b BStr>>(
&mut self, &mut self,
section_name: &'b str, section_name: S,
subsection_name: Option<&'b str>, subsection_name: Option<S>,
key: &'b str, key: S,
) -> Result<Vec<&mut Cow<'a, str>>, GitConfigError<'b>> { ) -> Result<Vec<&mut Cow<'a, BStr>>, GitConfigError<'b>> {
let key = key.into();
let section_ids = self let section_ids = self
.get_section_ids_by_name_and_subname(section_name, subsection_name)? .get_section_ids_by_name_and_subname(
section_name.into(),
subsection_name.map(Into::into),
)?
.to_vec(); .to_vec();
let mut found_key = false; let mut found_key = false;
let values: Vec<&mut Cow<'a, str>> = self let values: Vec<&mut Cow<'a, BStr>> = self
.sections .sections
.iter_mut() .iter_mut()
.filter_map(|(k, v)| { .filter_map(|(k, v)| {
@ -474,8 +503,8 @@ impl<'a> GitConfig<'a> {
fn get_section_ids_by_name_and_subname<'b>( fn get_section_ids_by_name_and_subname<'b>(
&'a self, &'a self,
section_name: &'b str, section_name: &'b BStr,
subsection_name: Option<&'b str>, subsection_name: Option<&'b BStr>,
) -> Result<&[SectionId], GitConfigError<'b>> { ) -> Result<&[SectionId], GitConfigError<'b>> {
let section_ids = self let section_ids = self
.section_lookup_tree .section_lookup_tree
@ -488,7 +517,7 @@ impl<'a> GitConfig<'a> {
if let Some(subsect_name) = subsection_name { if let Some(subsect_name) = subsection_name {
for node in section_ids { for node in section_ids {
if let LookupTreeNode::NonTerminal(subsection_lookup) = node { if let LookupTreeNode::NonTerminal(subsection_lookup) = node {
maybe_ids = subsection_lookup.get(subsect_name); maybe_ids = subsection_lookup.get(subsect_name.into());
break; break;
} }
} }
@ -505,12 +534,12 @@ impl<'a> GitConfig<'a> {
.ok_or(GitConfigError::SubSectionDoesNotExist(subsection_name)) .ok_or(GitConfigError::SubSectionDoesNotExist(subsection_name))
} }
pub fn set_raw_value<'b>( pub fn set_raw_value<'b, S: Into<&'b BStr>>(
&mut self, &mut self,
section_name: &'b str, section_name: S,
subsection_name: Option<&'b str>, subsection_name: Option<S>,
key: &'b str, key: S,
new_value: impl Into<Cow<'a, str>>, new_value: impl Into<Cow<'a, BStr>>,
) -> Result<(), GitConfigError<'b>> { ) -> Result<(), GitConfigError<'b>> {
let value = self.get_raw_value_mut(section_name, subsection_name, key)?; let value = self.get_raw_value_mut(section_name, subsection_name, key)?;
*value = new_value.into(); *value = new_value.into();
@ -530,12 +559,12 @@ impl<'a> GitConfig<'a> {
/// todo: examples and errors /// todo: examples and errors
/// ///
/// [`get_raw_multi_value_mut`]: Self::get_raw_multi_value_mut /// [`get_raw_multi_value_mut`]: Self::get_raw_multi_value_mut
pub fn set_raw_multi_value<'b>( pub fn set_raw_multi_value<'b, S: Into<&'b BStr>>(
&mut self, &mut self,
section_name: &'b str, section_name: S,
subsection_name: Option<&'b str>, subsection_name: Option<S>,
key: &'b str, key: S,
new_values: Vec<Cow<'a, str>>, new_values: Vec<Cow<'a, BStr>>,
) -> Result<(), GitConfigError<'b>> { ) -> Result<(), GitConfigError<'b>> {
let values = self.get_raw_multi_value_mut(section_name, subsection_name, key)?; let values = self.get_raw_multi_value_mut(section_name, subsection_name, key)?;
for (old, new) in values.into_iter().zip(new_values) { for (old, new) in values.into_iter().zip(new_values) {
@ -573,6 +602,7 @@ impl Display for GitConfig<'_> {
#[cfg(test)] #[cfg(test)]
mod from_parser { mod from_parser {
use super::*; use super::*;
use crate::test_util::*;
#[test] #[test]
fn parse_empty() { fn parse_empty() {
@ -589,14 +619,7 @@ mod from_parser {
let mut config = GitConfig::from_str("[core]\na=b\nc=d").unwrap(); let mut config = GitConfig::from_str("[core]\na=b\nc=d").unwrap();
let expected_separators = { let expected_separators = {
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert( map.insert(SectionId(0), section_header("core", None));
SectionId(0),
ParsedSectionHeader {
name: "core".into(),
separator: None,
subsection_name: None,
},
);
map map
}; };
assert_eq!(config.section_headers, expected_separators); assert_eq!(config.section_headers, expected_separators);
@ -604,7 +627,7 @@ mod from_parser {
let expected_lookup_tree = { let expected_lookup_tree = {
let mut tree = HashMap::new(); let mut tree = HashMap::new();
tree.insert( tree.insert(
Cow::Borrowed("core"), Cow::Borrowed("core".into()),
vec![LookupTreeNode::Terminal(vec![SectionId(0)])], vec![LookupTreeNode::Terminal(vec![SectionId(0)])],
); );
tree tree
@ -615,14 +638,14 @@ mod from_parser {
sections.insert( sections.insert(
SectionId(0), SectionId(0),
vec![ vec![
Event::Newline(Cow::Borrowed("\n")), newline_event(),
Event::Key(Cow::Borrowed("a")), name_event("a"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
Event::Value(Cow::Borrowed("b")), value_event("b"),
Event::Newline(Cow::Borrowed("\n")), newline_event(),
Event::Key(Cow::Borrowed("c")), name_event("c"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
Event::Value(Cow::Borrowed("d")), value_event("d"),
], ],
); );
sections sections
@ -636,14 +659,7 @@ mod from_parser {
let mut config = GitConfig::from_str("[core.subsec]\na=b\nc=d").unwrap(); let mut config = GitConfig::from_str("[core.subsec]\na=b\nc=d").unwrap();
let expected_separators = { let expected_separators = {
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert( map.insert(SectionId(0), section_header("core", (".", "subsec")));
SectionId(0),
ParsedSectionHeader {
name: "core".into(),
separator: Some(".".into()),
subsection_name: Some("subsec".into()),
},
);
map map
}; };
assert_eq!(config.section_headers, expected_separators); assert_eq!(config.section_headers, expected_separators);
@ -651,9 +667,9 @@ mod from_parser {
let expected_lookup_tree = { let expected_lookup_tree = {
let mut tree = HashMap::new(); let mut tree = HashMap::new();
let mut inner_tree = HashMap::new(); let mut inner_tree = HashMap::new();
inner_tree.insert(Cow::Borrowed("subsec"), vec![SectionId(0)]); inner_tree.insert(Cow::Borrowed("subsec".into()), vec![SectionId(0)]);
tree.insert( tree.insert(
Cow::Borrowed("core"), Cow::Borrowed("core".into()),
vec![LookupTreeNode::NonTerminal(inner_tree)], vec![LookupTreeNode::NonTerminal(inner_tree)],
); );
tree tree
@ -664,14 +680,14 @@ mod from_parser {
sections.insert( sections.insert(
SectionId(0), SectionId(0),
vec![ vec![
Event::Newline(Cow::Borrowed("\n")), newline_event(),
Event::Key(Cow::Borrowed("a")), name_event("a"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
Event::Value(Cow::Borrowed("b")), value_event("b"),
Event::Newline(Cow::Borrowed("\n")), newline_event(),
Event::Key(Cow::Borrowed("c")), name_event("c"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
Event::Value(Cow::Borrowed("d")), value_event("d"),
], ],
); );
sections sections
@ -685,22 +701,8 @@ mod from_parser {
let mut config = GitConfig::from_str("[core]\na=b\nc=d\n[other]e=f").unwrap(); let mut config = GitConfig::from_str("[core]\na=b\nc=d\n[other]e=f").unwrap();
let expected_separators = { let expected_separators = {
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert( map.insert(SectionId(0), section_header("core", None));
SectionId(0), map.insert(SectionId(1), section_header("other", None));
ParsedSectionHeader {
name: "core".into(),
separator: None,
subsection_name: None,
},
);
map.insert(
SectionId(1),
ParsedSectionHeader {
name: "other".into(),
separator: None,
subsection_name: None,
},
);
map map
}; };
assert_eq!(config.section_headers, expected_separators); assert_eq!(config.section_headers, expected_separators);
@ -708,11 +710,11 @@ mod from_parser {
let expected_lookup_tree = { let expected_lookup_tree = {
let mut tree = HashMap::new(); let mut tree = HashMap::new();
tree.insert( tree.insert(
Cow::Borrowed("core"), Cow::Borrowed("core".into()),
vec![LookupTreeNode::Terminal(vec![SectionId(0)])], vec![LookupTreeNode::Terminal(vec![SectionId(0)])],
); );
tree.insert( tree.insert(
Cow::Borrowed("other"), Cow::Borrowed("other".into()),
vec![LookupTreeNode::Terminal(vec![SectionId(1)])], vec![LookupTreeNode::Terminal(vec![SectionId(1)])],
); );
tree tree
@ -723,24 +725,20 @@ mod from_parser {
sections.insert( sections.insert(
SectionId(0), SectionId(0),
vec![ vec![
Event::Newline(Cow::Borrowed("\n")), newline_event(),
Event::Key(Cow::Borrowed("a")), name_event("a"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
Event::Value(Cow::Borrowed("b")), value_event("b"),
Event::Newline(Cow::Borrowed("\n")), newline_event(),
Event::Key(Cow::Borrowed("c")), name_event("c"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
Event::Value(Cow::Borrowed("d")), value_event("d"),
Event::Newline(Cow::Borrowed("\n")), newline_event(),
], ],
); );
sections.insert( sections.insert(
SectionId(1), SectionId(1),
vec![ vec![name_event("e"), Event::KeyValueSeparator, value_event("f")],
Event::Key(Cow::Borrowed("e")),
Event::KeyValueSeparator,
Event::Value(Cow::Borrowed("f")),
],
); );
sections sections
}; };
@ -756,22 +754,8 @@ mod from_parser {
let mut config = GitConfig::from_str("[core]\na=b\nc=d\n[core]e=f").unwrap(); let mut config = GitConfig::from_str("[core]\na=b\nc=d\n[core]e=f").unwrap();
let expected_separators = { let expected_separators = {
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert( map.insert(SectionId(0), section_header("core", None));
SectionId(0), map.insert(SectionId(1), section_header("core", None));
ParsedSectionHeader {
name: "core".into(),
separator: None,
subsection_name: None,
},
);
map.insert(
SectionId(1),
ParsedSectionHeader {
name: "core".into(),
separator: None,
subsection_name: None,
},
);
map map
}; };
assert_eq!(config.section_headers, expected_separators); assert_eq!(config.section_headers, expected_separators);
@ -779,7 +763,7 @@ mod from_parser {
let expected_lookup_tree = { let expected_lookup_tree = {
let mut tree = HashMap::new(); let mut tree = HashMap::new();
tree.insert( tree.insert(
Cow::Borrowed("core"), Cow::Borrowed("core".into()),
vec![LookupTreeNode::Terminal(vec![SectionId(0), SectionId(1)])], vec![LookupTreeNode::Terminal(vec![SectionId(0), SectionId(1)])],
); );
tree tree
@ -790,24 +774,20 @@ mod from_parser {
sections.insert( sections.insert(
SectionId(0), SectionId(0),
vec![ vec![
Event::Newline(Cow::Borrowed("\n")), newline_event(),
Event::Key(Cow::Borrowed("a")), name_event("a"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
Event::Value(Cow::Borrowed("b")), value_event("b"),
Event::Newline(Cow::Borrowed("\n")), newline_event(),
Event::Key(Cow::Borrowed("c")), name_event("c"),
Event::KeyValueSeparator, Event::KeyValueSeparator,
Event::Value(Cow::Borrowed("d")), value_event("d"),
Event::Newline(Cow::Borrowed("\n")), newline_event(),
], ],
); );
sections.insert( sections.insert(
SectionId(1), SectionId(1),
vec![ vec![name_event("e"), Event::KeyValueSeparator, value_event("f")],
Event::Key(Cow::Borrowed("e")),
Event::KeyValueSeparator,
Event::Value(Cow::Borrowed("f")),
],
); );
sections sections
}; };
@ -828,11 +808,11 @@ mod get_raw_value {
let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap(); let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap();
assert_eq!( assert_eq!(
config.get_raw_value("core", None, "a"), config.get_raw_value("core", None, "a"),
Ok(&Cow::Borrowed("b")) Ok(&Cow::Borrowed("b".into()))
); );
assert_eq!( assert_eq!(
config.get_raw_value("core", None, "c"), config.get_raw_value("core", None, "c"),
Ok(&Cow::Borrowed("d")) Ok(&Cow::Borrowed("d".into()))
); );
} }
@ -841,7 +821,7 @@ mod get_raw_value {
let config = GitConfig::from_str("[core]\na=b\na=d").unwrap(); let config = GitConfig::from_str("[core]\na=b\na=d").unwrap();
assert_eq!( assert_eq!(
config.get_raw_value("core", None, "a"), config.get_raw_value("core", None, "a"),
Ok(&Cow::Borrowed("d")) Ok(&Cow::Borrowed("d".into()))
); );
} }
@ -850,7 +830,7 @@ mod get_raw_value {
let config = GitConfig::from_str("[core]\na=b\n[core]\na=d").unwrap(); let config = GitConfig::from_str("[core]\na=b\n[core]\na=d").unwrap();
assert_eq!( assert_eq!(
config.get_raw_value("core", None, "a"), config.get_raw_value("core", None, "a"),
Ok(&Cow::Borrowed("d")) Ok(&Cow::Borrowed("d".into()))
); );
} }
@ -859,7 +839,7 @@ mod get_raw_value {
let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap(); let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap();
assert_eq!( assert_eq!(
config.get_raw_value("foo", None, "a"), config.get_raw_value("foo", None, "a"),
Err(GitConfigError::SectionDoesNotExist("foo")) Err(GitConfigError::SectionDoesNotExist("foo".into()))
); );
} }
@ -868,7 +848,7 @@ mod get_raw_value {
let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap(); let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap();
assert_eq!( assert_eq!(
config.get_raw_value("core", Some("a"), "a"), config.get_raw_value("core", Some("a"), "a"),
Err(GitConfigError::SubSectionDoesNotExist(Some("a"))) Err(GitConfigError::SubSectionDoesNotExist(Some("a".into())))
); );
} }
@ -877,7 +857,7 @@ mod get_raw_value {
let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap(); let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap();
assert_eq!( assert_eq!(
config.get_raw_value("core", None, "aaaaaa"), config.get_raw_value("core", None, "aaaaaa"),
Err(GitConfigError::KeyDoesNotExist("aaaaaa")) Err(GitConfigError::KeyDoesNotExist("aaaaaa".into()))
); );
} }
@ -886,11 +866,11 @@ mod get_raw_value {
let config = GitConfig::from_str("[core]a=b\n[core.a]a=c").unwrap(); let config = GitConfig::from_str("[core]a=b\n[core.a]a=c").unwrap();
assert_eq!( assert_eq!(
config.get_raw_value("core", None, "a"), config.get_raw_value("core", None, "a"),
Ok(&Cow::Borrowed("b")) Ok(&Cow::Borrowed("b".into()))
); );
assert_eq!( assert_eq!(
config.get_raw_value("core", Some("a"), "a"), config.get_raw_value("core", Some("a"), "a"),
Ok(&Cow::Borrowed("c")) Ok(&Cow::Borrowed("c".into()))
); );
} }
} }
@ -913,7 +893,7 @@ mod get_raw_multi_value {
let config = GitConfig::from_str("[core]\na=b\na=c").unwrap(); let config = GitConfig::from_str("[core]\na=b\na=c").unwrap();
assert_eq!( assert_eq!(
config.get_raw_multi_value("core", None, "a").unwrap(), config.get_raw_multi_value("core", None, "a").unwrap(),
vec!["b", "c"] vec![&Cow::Borrowed("b"), &Cow::Borrowed("c")]
); );
} }
@ -922,7 +902,11 @@ mod get_raw_multi_value {
let config = GitConfig::from_str("[core]\na=b\na=c\n[core]a=d").unwrap(); let config = GitConfig::from_str("[core]\na=b\na=c\n[core]a=d").unwrap();
assert_eq!( assert_eq!(
config.get_raw_multi_value("core", None, "a").unwrap(), config.get_raw_multi_value("core", None, "a").unwrap(),
vec!["b", "c", "d"] vec![
&Cow::Borrowed("b"),
&Cow::Borrowed("c"),
&Cow::Borrowed("d")
]
); );
} }
@ -931,7 +915,7 @@ mod get_raw_multi_value {
let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap(); let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap();
assert_eq!( assert_eq!(
config.get_raw_multi_value("foo", None, "a"), config.get_raw_multi_value("foo", None, "a"),
Err(GitConfigError::SectionDoesNotExist("foo")) Err(GitConfigError::SectionDoesNotExist("foo".into()))
); );
} }
@ -940,7 +924,7 @@ mod get_raw_multi_value {
let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap(); let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap();
assert_eq!( assert_eq!(
config.get_raw_multi_value("core", Some("a"), "a"), config.get_raw_multi_value("core", Some("a"), "a"),
Err(GitConfigError::SubSectionDoesNotExist(Some("a"))) Err(GitConfigError::SubSectionDoesNotExist(Some("a".into())))
); );
} }
@ -949,7 +933,7 @@ mod get_raw_multi_value {
let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap(); let config = GitConfig::from_str("[core]\na=b\nc=d").unwrap();
assert_eq!( assert_eq!(
config.get_raw_multi_value("core", None, "aaaaaa"), config.get_raw_multi_value("core", None, "aaaaaa"),
Err(GitConfigError::KeyDoesNotExist("aaaaaa")) Err(GitConfigError::KeyDoesNotExist("aaaaaa".into()))
); );
} }
@ -958,11 +942,11 @@ mod get_raw_multi_value {
let config = GitConfig::from_str("[core]a=b\n[core.a]a=c").unwrap(); let config = GitConfig::from_str("[core]a=b\n[core.a]a=c").unwrap();
assert_eq!( assert_eq!(
config.get_raw_multi_value("core", None, "a").unwrap(), config.get_raw_multi_value("core", None, "a").unwrap(),
vec!["b"] vec![&Cow::Borrowed("b")]
); );
assert_eq!( assert_eq!(
config.get_raw_multi_value("core", Some("a"), "a").unwrap(), config.get_raw_multi_value("core", Some("a"), "a").unwrap(),
vec!["c"] vec![&Cow::Borrowed("c")]
); );
} }
@ -971,7 +955,11 @@ mod get_raw_multi_value {
let config = GitConfig::from_str("[core]\na=b\na=c\n[core]a=d\n[core]g=g").unwrap(); let config = GitConfig::from_str("[core]\na=b\na=c\n[core]a=d\n[core]g=g").unwrap();
assert_eq!( assert_eq!(
config.get_raw_multi_value("core", None, "a").unwrap(), config.get_raw_multi_value("core", None, "a").unwrap(),
vec!["b", "c", "d"] vec![
&Cow::Borrowed("b"),
&Cow::Borrowed("c"),
&Cow::Borrowed("d")
]
); );
} }
} }

View file

@ -10,3 +10,6 @@ pub mod values;
// pub use de::{from_str, Deserializer}; // pub use de::{from_str, Deserializer};
pub use error::{Error, Result}; pub use error::{Error, Result};
// pub use ser::{to_string, Serializer}; // pub use ser::{to_string, Serializer};
#[cfg(test)]
pub mod test_util;

File diff suppressed because it is too large Load diff

73
src/test_util.rs Normal file
View file

@ -0,0 +1,73 @@
use std::borrow::Cow;
use crate::parser::{Event, ParsedComment, ParsedSectionHeader};
pub fn section_header_event(
name: &str,
subsection: impl Into<Option<(&'static str, &'static str)>>,
) -> Event<'_> {
Event::SectionHeader(section_header(name, subsection))
}
pub fn section_header(
name: &str,
subsection: impl Into<Option<(&'static str, &'static str)>>,
) -> ParsedSectionHeader<'_> {
let name = Cow::Borrowed(name.into());
if let Some((separator, subsection_name)) = subsection.into() {
ParsedSectionHeader {
name,
separator: Some(Cow::Borrowed(separator.into())),
subsection_name: Some(Cow::Borrowed(subsection_name.into())),
}
} else {
ParsedSectionHeader {
name,
separator: None,
subsection_name: None,
}
}
}
pub(crate) fn name_event(name: &'static str) -> Event<'static> {
Event::Key(Cow::Borrowed(name.into()))
}
pub(crate) fn value_event(value: &'static str) -> Event<'static> {
Event::Value(Cow::Borrowed(value.into()))
}
pub(crate) fn value_not_done_event(value: &'static str) -> Event<'static> {
Event::ValueNotDone(Cow::Borrowed(value.into()))
}
pub(crate) fn value_done_event(value: &'static str) -> Event<'static> {
Event::ValueDone(Cow::Borrowed(value.into()))
}
pub(crate) fn newline_event() -> Event<'static> {
newline_custom_event("\n")
}
pub(crate) fn newline_custom_event(value: &'static str) -> Event<'static> {
Event::Newline(Cow::Borrowed(value.into()))
}
pub(crate) fn whitespace_event(value: &'static str) -> Event<'static> {
Event::Whitespace(Cow::Borrowed(value.into()))
}
pub(crate) fn separator_event() -> Event<'static> {
Event::KeyValueSeparator
}
pub(crate) fn comment_event(tag: char, msg: &'static str) -> Event<'static> {
Event::Comment(comment(tag, msg))
}
pub(crate) fn comment(comment_tag: char, comment: &'static str) -> ParsedComment<'static> {
ParsedComment {
comment_tag,
comment: Cow::Borrowed(comment.into()),
}
}

View file

@ -2,41 +2,51 @@ use std::borrow::Cow;
use serde_git_config::parser::{parse_from_str, Event, ParsedSectionHeader}; use serde_git_config::parser::{parse_from_str, Event, ParsedSectionHeader};
fn gen_section_header( pub fn section_header_event(
name: &str, name: &str,
subsection: impl Into<Option<(&'static str, &'static str)>>, subsection: impl Into<Option<(&'static str, &'static str)>>,
) -> Event<'_> { ) -> Event<'_> {
let name = Cow::Borrowed(name); Event::SectionHeader(section_header(name, subsection))
Event::SectionHeader(
if let Some((separator, subsection_name)) = subsection.into() {
ParsedSectionHeader {
name,
separator: Some(Cow::Borrowed(separator)),
subsection_name: Some(Cow::Borrowed(subsection_name)),
}
} else {
ParsedSectionHeader {
name,
separator: None,
subsection_name: None,
}
},
)
} }
pub fn section_header(
name: &str,
subsection: impl Into<Option<(&'static str, &'static str)>>,
) -> ParsedSectionHeader<'_> {
let name = Cow::Borrowed(name.into());
if let Some((separator, subsection_name)) = subsection.into() {
ParsedSectionHeader {
name,
separator: Some(Cow::Borrowed(separator.into())),
subsection_name: Some(Cow::Borrowed(subsection_name.into())),
}
} else {
ParsedSectionHeader {
name,
separator: None,
subsection_name: None,
}
}
}
fn name(name: &'static str) -> Event<'static> { fn name(name: &'static str) -> Event<'static> {
Event::Key(Cow::Borrowed(name)) Event::Key(Cow::Borrowed(name.into()))
} }
fn value(value: &'static str) -> Event<'static> { fn value(value: &'static str) -> Event<'static> {
Event::Value(Cow::Borrowed(value)) Event::Value(Cow::Borrowed(value.into()))
} }
fn newline() -> Event<'static> { fn newline() -> Event<'static> {
Event::Newline(Cow::Borrowed("\n")) newline_custom("\n")
}
fn newline_custom(value: &'static str) -> Event<'static> {
Event::Newline(Cow::Borrowed(value.into()))
} }
fn whitespace(value: &'static str) -> Event<'static> { fn whitespace(value: &'static str) -> Event<'static> {
Event::Whitespace(Cow::Borrowed(value)) Event::Whitespace(Cow::Borrowed(value.into()))
} }
fn separator() -> Event<'static> { fn separator() -> Event<'static> {
@ -71,7 +81,7 @@ fn personal_config() {
.unwrap() .unwrap()
.into_vec(), .into_vec(),
vec![ vec![
gen_section_header("user", None), section_header_event("user", None),
newline(), newline(),
whitespace(" "), whitespace(" "),
@ -90,7 +100,7 @@ fn personal_config() {
value("Foo Bar"), value("Foo Bar"),
newline(), newline(),
gen_section_header("core", None), section_header_event("core", None),
newline(), newline(),
whitespace(" "), whitespace(" "),
@ -101,7 +111,7 @@ fn personal_config() {
value("input"), value("input"),
newline(), newline(),
gen_section_header("push", None), section_header_event("push", None),
newline(), newline(),
whitespace(" "), whitespace(" "),
@ -112,7 +122,7 @@ fn personal_config() {
value("simple"), value("simple"),
newline(), newline(),
gen_section_header("commit", None), section_header_event("commit", None),
newline(), newline(),
whitespace(" "), whitespace(" "),
@ -123,7 +133,7 @@ fn personal_config() {
value("true"), value("true"),
newline(), newline(),
gen_section_header("gpg", None), section_header_event("gpg", None),
newline(), newline(),
whitespace(" "), whitespace(" "),
@ -134,7 +144,7 @@ fn personal_config() {
value("gpg"), value("gpg"),
newline(), newline(),
gen_section_header("url", (" ", "ssh://git@github.com/")), section_header_event("url", (" ", "ssh://git@github.com/")),
newline(), newline(),
whitespace(" "), whitespace(" "),
@ -145,7 +155,7 @@ fn personal_config() {
value("\"github://\""), value("\"github://\""),
newline(), newline(),
gen_section_header("url", (" ", "ssh://git@git.eddie.sh/edward/")), section_header_event("url", (" ", "ssh://git@git.eddie.sh/edward/")),
newline(), newline(),
whitespace(" "), whitespace(" "),
@ -156,7 +166,7 @@ fn personal_config() {
value("\"gitea://\""), value("\"gitea://\""),
newline(), newline(),
gen_section_header("pull", None), section_header_event("pull", None),
newline(), newline(),
whitespace(" "), whitespace(" "),
@ -167,7 +177,7 @@ fn personal_config() {
value("only"), value("only"),
newline(), newline(),
gen_section_header("init", None), section_header_event("init", None),
newline(), newline(),
whitespace(" "), whitespace(" "),
@ -203,6 +213,6 @@ fn parse_whitespace() {
fn newline_events_are_merged() { fn newline_events_are_merged() {
assert_eq!( assert_eq!(
parse_from_str("\n\n\n\n\n").unwrap().into_vec(), parse_from_str("\n\n\n\n\n").unwrap().into_vec(),
vec![Event::Newline("\n\n\n\n\n".into())] vec![newline_custom("\n\n\n\n\n")]
); );
} }