diff --git a/src/diff.rs b/src/diff.rs index 7de5f55..08ce051 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -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(¶ms.from) { + let from_result = read_file_contents(¶ms.from); + let to_result = read_file_contents(¶ms.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>| 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![] @@ -113,10 +123,12 @@ pub fn compare_files(params: &Params, header: Option<&[u8]>) -> Outcome { vec![] } }; - let to_content = match read_file_contents(¶ms.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![] diff --git a/tests/by-util/test_diff.rs b/tests/by-util/test_diff.rs index 10fc0a1..595ed94 100644 --- a/tests/by-util/test_diff.rs +++ b/tests/by-util/test_diff.rs @@ -364,6 +364,34 @@ fn missing_file_as_empty_keeps_stdin_timestamp() -> Result<(), Box Result<(), Box> { + let tmp_dir = tempdir()?; + let missing1 = tmp_dir.path().join("missing1"); + let missing2 = tmp_dir.path().join("missing2"); + + #[cfg(not(windows))] + 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> { let tmp_dir = tempdir()?;