From 77738936443ec3e316c44a3785f8831b25b27ed7 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Sun, 7 Dec 2025 17:22:56 -0800 Subject: [PATCH 1/3] clippy: remove useless_vec This currently breaks CI in e.g. https://github.com/gitpatch-rs/gitpatch/actions/runs/20013661647/job/57387162629#step:5:70 --- src/ast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast.rs b/src/ast.rs index d923f10..48374c4 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -299,7 +299,7 @@ mod tests { range_hint: "", lines: vec![], }; - for (input, expected) in vec![ + for (input, expected) in [ ("", None), (" ", None), (" ", None), From fc727b96d1f825f4024f13cd75a21b728afd3de1 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Sun, 7 Dec 2025 17:05:57 -0800 Subject: [PATCH 2/3] Don't error on empty input Fixes #13 --- CHANGELOG.md | 4 ++++ src/ast.rs | 4 +++- src/parser.rs | 8 +++++++- tests/samples/empty.diff | 0 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 tests/samples/empty.diff 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 48374c4..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 /// diff --git a/src/parser.rs b/src/parser.rs index 487f3e2..9b19895 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -95,7 +95,7 @@ pub(crate) fn parse_multiple_patches(s: &str) -> Result>, ParseErr } fn multiple_patches(input: Input) -> IResult> { - many1(patch)(input) + many0(patch)(input) } fn patch(input: Input) -> IResult { @@ -424,6 +424,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 From 04fcfd6585a8e909d70e6e0ea4af20fea7fda7e1 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Sun, 7 Dec 2025 18:29:39 -0800 Subject: [PATCH 3/3] Simplify the check that multiple_patches consumes the whole file --- src/parser.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 9b19895..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> { - many0(patch)(input) + all_consuming(many0(patch))(input) } fn patch(input: Input) -> IResult {