diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
index 1e3a81f..7a52634 100644
--- a/.github/workflows/checks.yml
+++ b/.github/workflows/checks.yml
@@ -5,15 +5,33 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
+ # allow manual trigger
+ workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
test:
- runs-on: ubuntu-latest
+ name: Build and test for ${{matrix.rust}} on ${{matrix.os}}
+ runs-on: ${{matrix.os}}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ rust:
+ - 1.84.1 # pre edition2024
+ - 1.87
+ - stable
steps:
- uses: actions/checkout@v2
+
+ - uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: ${{matrix.rust}}
+ override: true
+
- name: Build lib
run: cargo build --verbose
- name: Run tests
@@ -22,13 +40,22 @@ jobs:
run: cargo build --example '*'
style:
- runs-on: ubuntu-latest
+ name: Style checks for ${{matrix.rust}} on ${{matrix.os}}
+ runs-on: ${{matrix.os}}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ rust:
+ - 1.84.1 # pre edition2024
+ - 1.87 # clippy became more picky after this
+ - stable
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
- toolchain: stable
+ toolchain: ${{matrix.rust}}
override: true
components: rustfmt, clippy
- name: fmt
diff --git a/src/parser.rs b/src/parser.rs
index 8c7b00b..487f3e2 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -68,7 +68,7 @@ fn consume_content_line(input: Input<'_>) -> IResult, &str> {
Ok((input, raw.fragment()))
}
-pub(crate) fn parse_single_patch(s: &str) -> Result> {
+pub(crate) fn parse_single_patch(s: &str) -> Result, ParseError<'_>> {
let (remaining_input, patch) = patch(Input::new(s))?;
// Parser should return an error instead of producing remaining input
assert!(
@@ -80,7 +80,7 @@ pub(crate) fn parse_single_patch(s: &str) -> Result> {
Ok(patch)
}
-pub(crate) fn parse_multiple_patches(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() {
@@ -94,11 +94,11 @@ pub(crate) fn parse_multiple_patches(s: &str) -> Result, ParseError<'
Ok(patches)
}
-fn multiple_patches(input: Input<'_>) -> IResult, Vec> {
+fn multiple_patches(input: Input) -> IResult> {
many1(patch)(input)
}
-fn patch(input: Input<'_>) -> IResult, Patch> {
+fn patch(input: Input) -> IResult {
if let Ok(patch) = binary_files_differ(input) {
return Ok(patch);
}
@@ -124,7 +124,7 @@ fn patch(input: Input<'_>) -> IResult, Patch> {
}
/// Recognize a "binary files XX and YY differ" line as an empty patch.
-fn binary_files_differ(input: Input<'_>) -> IResult, Patch> {
+fn binary_files_differ(input: Input) -> IResult {
// The names aren't quoted so this seems to require lookahead and then
// parsing the identified string.
let (input, (old, new)) = context(
@@ -162,7 +162,7 @@ fn binary_files_differ(input: Input<'_>) -> IResult, Patch> {
///
/// The `parse` function should handle rename diffs with similary index less than 100%, at least as per the test
/// `parses_file_renames_with_some_diff`.
-fn file_rename_only(input: Input<'_>) -> IResult, Patch> {
+fn file_rename_only(input: Input<'_>) -> IResult, Patch<'_>> {
let (rest, _parsed) = take_until("\nsimilarity index 100%\n")(input)?;
let (rest, _parsed) = tag("\nsimilarity index 100%\n")(rest)?;
@@ -188,7 +188,7 @@ fn file_rename_only(input: Input<'_>) -> IResult, Patch> {
}
// Header lines
-fn headers(input: Input<'_>) -> IResult, (File, File)> {
+fn headers(input: Input) -> IResult {
// Ignore any preamble lines in produced diffs
let (input, _) = take_until("--- ")(input)?;
let (input, _) = tag("--- ")(input)?;
@@ -200,7 +200,7 @@ fn headers(input: Input<'_>) -> IResult, (File, File)> {
Ok((input, (oldfile, newfile)))
}
-fn header_line_content(input: Input<'_>) -> IResult, File> {
+fn header_line_content(input: Input) -> IResult {
let (input, filename) = filename(input)?;
let (input, after) = opt(preceded(char('\t'), file_metadata))(input)?;
@@ -223,7 +223,7 @@ fn header_line_content(input: Input<'_>) -> IResult, File> {
}
// Hunks of the file differences
-fn chunks(input: Input<'_>) -> IResult, Vec> {
+fn chunks(input: Input) -> IResult> {
many1(chunk)(input)
}
@@ -235,7 +235,6 @@ fn is_next_header(input: Input<'_>) -> bool {
|| input.starts_with("@@ ")
}
-
/// Looks for lines starting with + or - or space, but not +++ or ---. Not a foolproof check.
///
/// For example, if someone deletes a line that was using the pre-decrement (--) operator or adds a
@@ -264,7 +263,7 @@ fn is_next_header(input: Input<'_>) -> bool {
///FIXME: Use the ranges in the chunk header to figure out how many chunk lines to parse. Will need
/// to figure out how to count in nom more robustly than many1!(). Maybe using switch!()?
///FIXME: The test_parse_triple_plus_minus_hack test will no longer panic when this is fixed.
-fn chunk(input: Input<'_>) -> IResult, Hunk> {
+fn chunk(input: Input) -> IResult {
let (input, ranges) = chunk_header(input)?;
// Parse chunk lines, using the range information to guide parsing
@@ -338,11 +337,11 @@ fn no_newline_indicator(input: Input<'_>) -> IResult, bool> {
)(input)
}
-fn filename(input: Input<'_>) -> IResult, Cow> {
+fn filename(input: Input) -> IResult> {
alt((quoted, bare))(input)
}
-fn file_metadata(input: Input<'_>) -> IResult, Cow> {
+fn file_metadata(input: Input) -> IResult> {
alt((
quoted,
map(not_line_ending, |data: Input<'_>| {
@@ -351,28 +350,28 @@ fn file_metadata(input: Input<'_>) -> IResult, Cow> {
))(input)
}
-fn quoted(input: Input<'_>) -> IResult, Cow> {
+fn quoted(input: Input) -> IResult> {
delimited(char('\"'), unescaped_str, char('\"'))(input)
}
-fn bare(input: Input<'_>) -> IResult, Cow> {
+fn bare(input: Input) -> IResult> {
map(is_not("\t\r\n"), |data: Input<'_>| {
Cow::Borrowed(*data.fragment())
})(input)
}
-fn unescaped_str(input: Input<'_>) -> IResult, Cow> {
+fn unescaped_str(input: Input) -> IResult> {
let (input, raw) = many1(alt((unescaped_char, escaped_char)))(input)?;
Ok((input, raw.into_iter().collect::>()))
}
// Parses an unescaped character
-fn unescaped_char(input: Input<'_>) -> IResult, char> {
+fn unescaped_char(input: Input) -> IResult {
none_of("\0\n\r\t\\\"")(input)
}
// Parses an escaped character and returns its unescaped equivalent
-fn escaped_char(input: Input<'_>) -> IResult, char> {
+fn escaped_char(input: Input) -> IResult {
map(preceded(char('\\'), one_of(r#"0nrt"\"#)), |ch| match ch {
'0' => '\0',
'n' => '\n',
diff --git a/tests/parse_samples.rs b/tests/parse_samples.rs
index aac580d..50e223a 100644
--- a/tests/parse_samples.rs
+++ b/tests/parse_samples.rs
@@ -20,6 +20,7 @@ fn parse_samples() {
// Make sure that the patch file we produce parses to the same information as the original
// patch file.
+ #[allow(clippy::format_collect)] // Display::fmt is the only way to resolve Patch->str
let patch_file: String = patches.iter().map(|patch| format!("{}\n", patch)).collect();
println!("{}", patch_file);
let patches2 = Patch::from_multiple(&patch_file).unwrap_or_else(|err| {
@@ -50,10 +51,8 @@ fn parse_wild_samples() {
// Make sure that the patch file we produce parses to the same information as the original
// patch file.
- let patch_file: String = patches
- .iter()
- .map(|patch| format!("{}\n", patch))
- .collect();
+ #[allow(clippy::format_collect)] // Display::fmt is the only way to resolve Patch->str
+ let patch_file: String = patches.iter().map(|patch| format!("{}\n", patch)).collect();
let patches2 = Patch::from_multiple(&patch_file).unwrap_or_else(|err| {
panic!(
diff --git a/tests/wild-samples/.gitattributes b/tests/wild-samples/.gitattributes
new file mode 100644
index 0000000..3e07e53
--- /dev/null
+++ b/tests/wild-samples/.gitattributes
@@ -0,0 +1,2 @@
+# Disable text processing on these wild sample - eg preserve their wild CRLF format
+*.patch -text