diff --git a/CHANGELOG.md b/CHANGELOG.md index 537c263..a43dde0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # CHANGELOG ## [Unreleased] +### Breaking + +- `Patch::from_multiple` no longer returns an error on an input that contains no patches, including an empty string. It instead returns an empty vector. + ### Changed ## [v0.7] diff --git a/src/ast.rs b/src/ast.rs index d923f10..332b856 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -81,7 +81,9 @@ impl<'a> Patch<'a> { } /// Attempt to parse as many patches as possible from the given string. This is useful for when - /// you have a complete diff of many files. String must contain at least one patch. + /// you have a complete diff of many files. + /// + /// It is not an error if the string contains no patches: this returns an empty vector. /// /// # Example /// @@ -299,7 +301,7 @@ mod tests { range_hint: "", lines: vec![], }; - for (input, expected) in vec![ + for (input, expected) in [ ("", None), (" ", None), (" ", None), diff --git a/src/parser.rs b/src/parser.rs index 487f3e2..dd1b2c9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2,13 +2,12 @@ use std::borrow::Cow; use std::error::Error; use chrono::DateTime; -use nom::combinator::verify; use nom::*; use nom::{ branch::alt, bytes::complete::{is_not, tag, take_until}, character::complete::{char, digit1, line_ending, none_of, not_line_ending, one_of}, - combinator::{map, map_opt, not, opt}, + combinator::{all_consuming, map, map_opt, not, opt, verify}, error::context, multi::{many0, many1}, sequence::{delimited, preceded, terminated, tuple}, @@ -82,20 +81,15 @@ pub(crate) fn parse_single_patch(s: &str) -> Result, ParseError<'_>> { pub(crate) fn parse_multiple_patches(s: &str) -> Result>, ParseError<'_>> { let (remaining_input, patches) = multiple_patches(Input::new(s))?; - // Parser should return an error instead of producing remaining input - if !remaining_input.fragment().is_empty() { - return Err(ParseError { - line: remaining_input.location_line(), - offset: remaining_input.location_offset(), - fragment: remaining_input.fragment(), - kind: nom::error::ErrorKind::Eof, - }); - } + debug_assert!( + remaining_input.fragment().is_empty(), + "all_consuming should not have left over input" + ); Ok(patches) } fn multiple_patches(input: Input) -> IResult> { - many1(patch)(input) + all_consuming(many0(patch))(input) } fn patch(input: Input) -> IResult { @@ -424,6 +418,12 @@ mod tests { Ok(()) } + #[test] + fn test_empty_input() -> ParseResult<'static, ()> { + test_parser!(multiple_patches("") -> @("", Vec::new())); + Ok(()) + } + #[test] fn test_filename() -> ParseResult<'static, ()> { // bare diff --git a/tests/samples/empty.diff b/tests/samples/empty.diff new file mode 100644 index 0000000..e69de29