diff --git a/Cargo.lock b/Cargo.lock index 0b5e50b..be5acdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,9 +33,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "winnow" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index a72f268..bf902c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,5 +43,5 @@ pre-release-replacements = [ ] [dependencies] -winnow = "0.4.0" +winnow = "0.5.0" itertools = "0.11" diff --git a/src/param.rs b/src/param.rs index 2b0a9e6..28b6d30 100644 --- a/src/param.rs +++ b/src/param.rs @@ -55,8 +55,8 @@ impl<'s> Iterator for ConfigParametersIter<'s> { fn next(&mut self) -> Option { // See git's config.c's `parse_config_env_list` - let (values, key) = crate::quote::sq_dequote_step(self.values.trim_start()).ok()?; - self.values = values; + self.values = self.values.trim_start(); + let key = crate::quote::sq_dequote(&mut self.values).ok()?; if let Some(values) = self.values.strip_prefix('=') { // new-style 'key'='value' @@ -68,8 +68,7 @@ impl<'s> Iterator for ConfigParametersIter<'s> { self.values = values; Some((key, None)) } else { - let (values, value) = crate::quote::sq_dequote_step(self.values).ok()?; - self.values = values; + let value = crate::quote::sq_dequote(&mut self.values).ok()?; Some((key, Some(value))) } } else { diff --git a/src/quote.rs b/src/quote.rs index 1234c31..e68c71b 100644 --- a/src/quote.rs +++ b/src/quote.rs @@ -1,22 +1,35 @@ use std::borrow::Cow; use itertools::Itertools; -use winnow::branch::*; -use winnow::bytes::*; -use winnow::multi::*; +use winnow::combinator::*; use winnow::prelude::*; -use winnow::sequence::*; +use winnow::token::*; -pub fn sq_dequote_step(input: &str) -> IResult<&str, Cow> { +#[derive(Debug)] +pub struct QuoteError; + +impl std::fmt::Display for QuoteError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "invalid quoting") + } +} + +impl std::error::Error for QuoteError {} + +pub fn sq_dequote_step<'i>(input: &mut &'i str) -> Result, QuoteError> { + sq_dequote.parse_next(input).map_err(|_e| QuoteError) +} + +pub fn sq_dequote<'i>(input: &mut &'i str) -> PResult, ()> { // See git's quote.c's `sq_dequote_step` alt((sq_dequote_escaped, sq_dequote_no_escaped)).parse_next(input) } -fn sq_dequote_escaped(input: &str) -> IResult<&str, Cow> { +fn sq_dequote_escaped<'i>(input: &mut &'i str) -> PResult, ()> { ( sq_dequote_section, sq_dequote_trail, - many0(sq_dequote_trail), + repeat(0.., sq_dequote_trail), ) .map(|(start, trail, mut trails): (_, _, Vec<_>)| { trails.insert(0, trail); @@ -27,22 +40,22 @@ fn sq_dequote_escaped(input: &str) -> IResult<&str, Cow> { .parse_next(input) } -fn sq_dequote_no_escaped(input: &str) -> IResult<&str, Cow> { +fn sq_dequote_no_escaped<'i>(input: &mut &'i str) -> PResult, ()> { sq_dequote_section.map(Cow::Borrowed).parse_next(input) } -fn sq_dequote_section(input: &str) -> IResult<&str, &str> { - terminated(preceded('\'', take_while0(|c| c != '\'')), '\'').parse_next(input) +fn sq_dequote_section<'i>(input: &mut &'i str) -> PResult<&'i str, ()> { + terminated(preceded('\'', take_while(0.., |c| c != '\'')), '\'').parse_next(input) } -fn sq_dequote_trail(input: &str) -> IResult<&str, [&str; 2]> { +fn sq_dequote_trail<'i>(input: &mut &'i str) -> PResult<[&'i str; 2], ()> { (escaped, sq_dequote_section) .map(|(e, s)| [e, s]) .parse_next(input) } -fn escaped(input: &str) -> IResult<&str, &str> { - preceded('\\', one_of("'!").recognize()).parse_next(input) +fn escaped<'i>(input: &mut &'i str) -> PResult<&'i str, ()> { + preceded('\\', one_of(['\'', '!']).recognize()).parse_next(input) } #[cfg(test)] @@ -53,7 +66,7 @@ mod test_sq_dequote_step { fn word() { let fixture = "'name'"; let expected = Cow::Borrowed("name"); - let (_, actual) = sq_dequote_step(fixture).unwrap(); + let (_, actual) = sq_dequote.parse_peek(fixture).unwrap(); assert_eq!(actual, expected); } @@ -61,7 +74,7 @@ mod test_sq_dequote_step { fn space() { let fixture = "'a b'"; let expected = Cow::Borrowed("a b"); - let (_, actual) = sq_dequote_step(fixture).unwrap(); + let (_, actual) = sq_dequote.parse_peek(fixture).unwrap(); assert_eq!(actual, expected); } @@ -69,7 +82,7 @@ mod test_sq_dequote_step { fn sq_escaped() { let fixture = "'a'\\''b'"; let expected: Cow = Cow::Owned("a'b".into()); - let (_, actual) = sq_dequote_step(fixture).unwrap(); + let (_, actual) = sq_dequote.parse_peek(fixture).unwrap(); assert_eq!(actual, expected); } @@ -77,7 +90,7 @@ mod test_sq_dequote_step { fn exclamation_escaped() { let fixture = "'a'\\!'b'"; let expected: Cow = Cow::Owned("a!b".into()); - let (_, actual) = sq_dequote_step(fixture).unwrap(); + let (_, actual) = sq_dequote.parse_peek(fixture).unwrap(); assert_eq!(actual, expected); } }