Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
35 changes: 17 additions & 18 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fn consume_content_line(input: Input<'_>) -> IResult<Input<'_>, &str> {
Ok((input, raw.fragment()))
}

pub(crate) fn parse_single_patch(s: &str) -> Result<Patch, ParseError<'_>> {
pub(crate) fn parse_single_patch(s: &str) -> Result<Patch<'_>, ParseError<'_>> {
let (remaining_input, patch) = patch(Input::new(s))?;
// Parser should return an error instead of producing remaining input
assert!(
Expand All @@ -80,7 +80,7 @@ pub(crate) fn parse_single_patch(s: &str) -> Result<Patch, ParseError<'_>> {
Ok(patch)
}

pub(crate) fn parse_multiple_patches(s: &str) -> Result<Vec<Patch>, ParseError<'_>> {
pub(crate) fn parse_multiple_patches(s: &str) -> Result<Vec<Patch<'_>>, 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() {
Expand All @@ -94,11 +94,11 @@ pub(crate) fn parse_multiple_patches(s: &str) -> Result<Vec<Patch>, ParseError<'
Ok(patches)
}

fn multiple_patches(input: Input<'_>) -> IResult<Input<'_>, Vec<Patch>> {
fn multiple_patches(input: Input) -> IResult<Input, Vec<Patch>> {
many1(patch)(input)
}

fn patch(input: Input<'_>) -> IResult<Input<'_>, Patch> {
fn patch(input: Input) -> IResult<Input, Patch> {
if let Ok(patch) = binary_files_differ(input) {
return Ok(patch);
}
Expand All @@ -124,7 +124,7 @@ fn patch(input: Input<'_>) -> IResult<Input<'_>, Patch> {
}

/// Recognize a "binary files XX and YY differ" line as an empty patch.
fn binary_files_differ(input: Input<'_>) -> IResult<Input<'_>, Patch> {
fn binary_files_differ(input: Input) -> IResult<Input, Patch> {
// The names aren't quoted so this seems to require lookahead and then
// parsing the identified string.
let (input, (old, new)) = context(
Expand Down Expand Up @@ -162,7 +162,7 @@ fn binary_files_differ(input: Input<'_>) -> IResult<Input<'_>, 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<Input<'_>, Patch> {
fn file_rename_only(input: Input<'_>) -> IResult<Input<'_>, Patch<'_>> {
let (rest, _parsed) = take_until("\nsimilarity index 100%\n")(input)?;
let (rest, _parsed) = tag("\nsimilarity index 100%\n")(rest)?;

Expand All @@ -188,7 +188,7 @@ fn file_rename_only(input: Input<'_>) -> IResult<Input<'_>, Patch> {
}

// Header lines
fn headers(input: Input<'_>) -> IResult<Input<'_>, (File, File)> {
fn headers(input: Input) -> IResult<Input, (File, File)> {
// Ignore any preamble lines in produced diffs
let (input, _) = take_until("--- ")(input)?;
let (input, _) = tag("--- ")(input)?;
Expand All @@ -200,7 +200,7 @@ fn headers(input: Input<'_>) -> IResult<Input<'_>, (File, File)> {
Ok((input, (oldfile, newfile)))
}

fn header_line_content(input: Input<'_>) -> IResult<Input<'_>, File> {
fn header_line_content(input: Input) -> IResult<Input, File> {
let (input, filename) = filename(input)?;
let (input, after) = opt(preceded(char('\t'), file_metadata))(input)?;

Expand All @@ -223,7 +223,7 @@ fn header_line_content(input: Input<'_>) -> IResult<Input<'_>, File> {
}

// Hunks of the file differences
fn chunks(input: Input<'_>) -> IResult<Input<'_>, Vec<Hunk>> {
fn chunks(input: Input) -> IResult<Input, Vec<Hunk>> {
many1(chunk)(input)
}

Expand All @@ -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
Expand Down Expand Up @@ -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<Input<'_>, Hunk> {
fn chunk(input: Input) -> IResult<Input, Hunk> {
let (input, ranges) = chunk_header(input)?;

// Parse chunk lines, using the range information to guide parsing
Expand Down Expand Up @@ -338,11 +337,11 @@ fn no_newline_indicator(input: Input<'_>) -> IResult<Input<'_>, bool> {
)(input)
}

fn filename(input: Input<'_>) -> IResult<Input<'_>, Cow<str>> {
fn filename(input: Input) -> IResult<Input, Cow<str>> {
alt((quoted, bare))(input)
}

fn file_metadata(input: Input<'_>) -> IResult<Input<'_>, Cow<str>> {
fn file_metadata(input: Input) -> IResult<Input, Cow<str>> {
alt((
quoted,
map(not_line_ending, |data: Input<'_>| {
Expand All @@ -351,28 +350,28 @@ fn file_metadata(input: Input<'_>) -> IResult<Input<'_>, Cow<str>> {
))(input)
}

fn quoted(input: Input<'_>) -> IResult<Input<'_>, Cow<str>> {
fn quoted(input: Input) -> IResult<Input, Cow<str>> {
delimited(char('\"'), unescaped_str, char('\"'))(input)
}

fn bare(input: Input<'_>) -> IResult<Input<'_>, Cow<str>> {
fn bare(input: Input) -> IResult<Input, Cow<str>> {
map(is_not("\t\r\n"), |data: Input<'_>| {
Cow::Borrowed(*data.fragment())
})(input)
}

fn unescaped_str(input: Input<'_>) -> IResult<Input<'_>, Cow<str>> {
fn unescaped_str(input: Input) -> IResult<Input, Cow<str>> {
let (input, raw) = many1(alt((unescaped_char, escaped_char)))(input)?;
Ok((input, raw.into_iter().collect::<Cow<str>>()))
}

// Parses an unescaped character
fn unescaped_char(input: Input<'_>) -> IResult<Input<'_>, char> {
fn unescaped_char(input: Input) -> IResult<Input, char> {
none_of("\0\n\r\t\\\"")(input)
}

// Parses an escaped character and returns its unescaped equivalent
fn escaped_char(input: Input<'_>) -> IResult<Input<'_>, char> {
fn escaped_char(input: Input) -> IResult<Input, char> {
map(preceded(char('\\'), one_of(r#"0nrt"\"#)), |ch| match ch {
'0' => '\0',
'n' => '\n',
Expand Down
7 changes: 3 additions & 4 deletions tests/parse_samples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand Down Expand Up @@ -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!(
Expand Down
2 changes: 2 additions & 0 deletions tests/wild-samples/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Disable text processing on these wild sample - eg preserve their wild CRLF format
*.patch -text
Loading