Skip to content
Open
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
20 changes: 16 additions & 4 deletions src/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,20 @@ pub fn compare_files(params: &Params, header: Option<&[u8]>) -> Outcome {
let mut io_error = false;
let mut from_missing = false;
let mut to_missing = false;
let from_content = match read_file_contents(&params.from) {
let from_result = read_file_contents(&params.from);
let to_result = read_file_contents(&params.to);
// Like GNU diff, -N and --unidirectional-new-file only treat a missing
// operand as empty when the other one exists, so two missing files are
// still reported as errors.
let is_missing =
|r: &io::Result<Vec<u8>>| matches!(r, Err(e) if e.kind() == io::ErrorKind::NotFound);
let both_missing = is_missing(&from_result) && is_missing(&to_result);
let from_content = match from_result {
Ok(from_content) => from_content,
Err(e)
if e.kind() == io::ErrorKind::NotFound && params.missing_file != MissingFile::Error =>
if e.kind() == io::ErrorKind::NotFound
&& params.missing_file != MissingFile::Error
&& !both_missing =>
{
from_missing = true;
vec![]
Expand All @@ -113,10 +123,12 @@ pub fn compare_files(params: &Params, header: Option<&[u8]>) -> Outcome {
vec![]
}
};
let to_content = match read_file_contents(&params.to) {
let to_content = match to_result {
Ok(to_content) => to_content,
Err(e)
if e.kind() == io::ErrorKind::NotFound && params.missing_file == MissingFile::Empty =>
if e.kind() == io::ErrorKind::NotFound
&& params.missing_file == MissingFile::Empty
&& !both_missing =>
{
to_missing = true;
vec![]
Expand Down
28 changes: 28 additions & 0 deletions tests/by-util/test_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,34 @@ fn missing_file_as_empty_keeps_stdin_timestamp() -> Result<(), Box<dyn std::erro
Ok(())
}

#[test]
fn new_file_both_missing_is_an_error() -> Result<(), Box<dyn std::error::Error>> {
let tmp_dir = tempdir()?;
let missing1 = tmp_dir.path().join("missing1");
let missing2 = tmp_dir.path().join("missing2");

#[cfg(not(windows))]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please squash the windows fixup commit into the first one :)

let error_message = "No such file or directory";
#[cfg(windows)]
let error_message = "The system cannot find the file specified.";

for option in ["-N", "--new-file", "--unidirectional-new-file"] {
let mut cmd = cargo_bin_cmd!("diffutils");
cmd.arg("diff").arg(option).arg(&missing1).arg(&missing2);
cmd.assert()
.code(predicate::eq(2))
.failure()
.stdout(predicate::str::is_empty())
.stderr(predicate::str::contains(format!(
"missing1: {error_message}"
)))
.stderr(predicate::str::contains(format!(
"missing2: {error_message}"
)));
}
Ok(())
}

#[test]
fn binary_files() -> Result<(), Box<dyn std::error::Error>> {
let tmp_dir = tempdir()?;
Expand Down
Loading