Skip to content

Commit 6145ab7

Browse files
committed
diff: add -N and --unidirectional-new-file
1 parent a7a66e7 commit 6145ab7

3 files changed

Lines changed: 74 additions & 1 deletion

File tree

‎src/diff.rs‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// For the full copyright and license information, please view the LICENSE-*
44
// files that was distributed with this source code.
55

6-
use crate::params::{parse_params, Format};
6+
use crate::params::{parse_params, Format, MissingFile};
77
use crate::utils::report_failure_to_read_input_file;
88
use crate::{context_diff, ed_diff, normal_diff, side_diff, unified_diff};
99
use std::env::ArgsOs;
@@ -52,6 +52,11 @@ pub fn main(opts: Peekable<ArgsOs>) -> ExitCode {
5252
let mut io_error = false;
5353
let from_content = match read_file_contents(&params.from) {
5454
Ok(from_content) => from_content,
55+
Err(e)
56+
if e.kind() == io::ErrorKind::NotFound && params.missing_file != MissingFile::Error =>
57+
{
58+
vec![]
59+
}
5560
Err(e) => {
5661
report_failure_to_read_input_file(&params.executable, &params.from, &e);
5762
io_error = true;
@@ -60,6 +65,11 @@ pub fn main(opts: Peekable<ArgsOs>) -> ExitCode {
6065
};
6166
let to_content = match read_file_contents(&params.to) {
6267
Ok(to_content) => to_content,
68+
Err(e)
69+
if e.kind() == io::ErrorKind::NotFound && params.missing_file == MissingFile::Empty =>
70+
{
71+
vec![]
72+
}
6373
Err(e) => {
6474
report_failure_to_read_input_file(&params.executable, &params.to, &e);
6575
io_error = true;

‎src/params.rs‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@ pub enum Format {
1414
SideBySide,
1515
}
1616

17+
/// How to handle an operand that does not exist (`-N` and
18+
/// `--unidirectional-new-file`).
19+
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
20+
pub enum MissingFile {
21+
/// A missing operand is an error.
22+
#[default]
23+
Error,
24+
/// A missing operand is treated as an empty file.
25+
Empty,
26+
/// Only a missing first operand is treated as an empty file.
27+
EmptyIfFirst,
28+
}
29+
1730
#[derive(Clone, Debug, Eq, PartialEq)]
1831
pub struct Params {
1932
pub executable: OsString,
@@ -26,6 +39,7 @@ pub struct Params {
2639
pub expand_tabs: bool,
2740
pub tabsize: usize,
2841
pub width: usize,
42+
pub missing_file: MissingFile,
2943
}
3044

3145
impl Default for Params {
@@ -41,6 +55,7 @@ impl Default for Params {
4155
expand_tabs: false,
4256
tabsize: 8,
4357
width: 130,
58+
missing_file: MissingFile::default(),
4459
}
4560
}
4661
}
@@ -87,6 +102,16 @@ pub fn parse_params<I: Iterator<Item = OsString>>(mut opts: Peekable<I>) -> Resu
87102
params.brief = true;
88103
continue;
89104
}
105+
if param == "-N" || param == "--new-file" {
106+
params.missing_file = MissingFile::Empty;
107+
continue;
108+
}
109+
if param == "--unidirectional-new-file" {
110+
if params.missing_file != MissingFile::Empty {
111+
params.missing_file = MissingFile::EmptyIfFirst;
112+
}
113+
continue;
114+
}
90115
if param == "-t" || param == "--expand-tabs" {
91116
params.expand_tabs = true;
92117
continue;

‎tests/by-util/test_diff.rs‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,44 @@ fn read_from_stdin() -> Result<(), Box<dyn std::error::Error>> {
206206
Ok(())
207207
}
208208

209+
#[test]
210+
fn missing_file_as_empty() -> Result<(), Box<dyn std::error::Error>> {
211+
let tmp_dir = tempdir()?;
212+
let present = tmp_dir.path().join("present");
213+
let absent = tmp_dir.path().join("absent");
214+
std::fs::write(&present, "kiwi\nmango\n")?;
215+
216+
for option in ["-N", "--new-file", "--unidirectional-new-file"] {
217+
let mut cmd = cargo_bin_cmd!("diffutils");
218+
cmd.arg("diff").arg(option).arg(&absent).arg(&present);
219+
cmd.assert()
220+
.code(predicate::eq(1))
221+
.failure()
222+
.stdout("0a1,2\n> kiwi\n> mango\n");
223+
}
224+
225+
let mut cmd = cargo_bin_cmd!("diffutils");
226+
cmd.arg("diff").arg("-N").arg(&present).arg(&absent);
227+
cmd.assert()
228+
.code(predicate::eq(1))
229+
.failure()
230+
.stdout("1,2d0\n< kiwi\n< mango\n");
231+
232+
// Only a missing first operand is accepted here.
233+
let mut cmd = cargo_bin_cmd!("diffutils");
234+
cmd.arg("diff")
235+
.arg("--unidirectional-new-file")
236+
.arg(&present)
237+
.arg(&absent);
238+
cmd.assert().code(predicate::eq(2)).failure();
239+
240+
// Without the option, a missing file is trouble.
241+
let mut cmd = cargo_bin_cmd!("diffutils");
242+
cmd.arg("diff").arg(&absent).arg(&present);
243+
cmd.assert().code(predicate::eq(2)).failure();
244+
Ok(())
245+
}
246+
209247
#[test]
210248
fn compare_file_to_directory() -> Result<(), Box<dyn std::error::Error>> {
211249
let tmp_dir = tempdir()?;

0 commit comments

Comments
 (0)