Skip to content

Commit c68d386

Browse files
oSoMoNsylvestre
authored andcommitted
Implement -s/--report-identical-files option (fixes #23)
1 parent a89f30a commit c68d386

File tree

5 files changed

+155
-0
lines changed

5 files changed

+155
-0
lines changed

Cargo.lock

+41
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ path = "src/main.rs"
1616

1717
[dependencies]
1818
diff = "0.1.10"
19+
same-file = "1.0.6"
1920

2021
[dev-dependencies]
2122
pretty_assertions = "1"

src/main.rs

+16
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,25 @@ fn main() -> ExitCode {
2828
to,
2929
context_count,
3030
format,
31+
report_identical_files,
3132
} = parse_params(opts).unwrap_or_else(|error| {
3233
eprintln!("{error}");
3334
exit(2);
3435
});
36+
// if from and to are the same file, no need to perform any comparison
37+
let maybe_report_identical_files = || {
38+
if report_identical_files {
39+
println!(
40+
"Files {} and {} are identical",
41+
from.to_string_lossy(),
42+
to.to_string_lossy(),
43+
)
44+
}
45+
};
46+
if same_file::is_same_file(&from, &to).unwrap_or(false) {
47+
maybe_report_identical_files();
48+
return ExitCode::SUCCESS;
49+
}
3550
// read files
3651
let from_content = match fs::read(&from) {
3752
Ok(from_content) => from_content,
@@ -71,6 +86,7 @@ fn main() -> ExitCode {
7186
};
7287
io::stdout().write_all(&result).unwrap();
7388
if result.is_empty() {
89+
maybe_report_identical_files();
7490
ExitCode::SUCCESS
7591
} else {
7692
ExitCode::from(1)

src/params.rs

+56
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub struct Params {
2525
pub to: OsString,
2626
pub format: Format,
2727
pub context_count: usize,
28+
pub report_identical_files: bool,
2829
}
2930

3031
pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params, String> {
@@ -38,6 +39,7 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
3839
let mut to = None;
3940
let mut format = None;
4041
let mut context_count = 3;
42+
let mut report_identical_files = false;
4143
while let Some(param) = opts.next() {
4244
if param == "--" {
4345
break;
@@ -52,6 +54,10 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
5254
}
5355
continue;
5456
}
57+
if param == "-s" || param == "--report-identical-files" {
58+
report_identical_files = true;
59+
continue;
60+
}
5561
let p = osstr_bytes(&param);
5662
if p.first() == Some(&b'-') && p.get(1) != Some(&b'-') {
5763
let mut bit = p[1..].iter().copied().peekable();
@@ -133,6 +139,7 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
133139
to,
134140
format,
135141
context_count,
142+
report_identical_files,
136143
})
137144
}
138145

@@ -150,6 +157,7 @@ mod tests {
150157
to: os("bar"),
151158
format: Format::Normal,
152159
context_count: 3,
160+
report_identical_files: false,
153161
}),
154162
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
155163
);
@@ -162,6 +170,7 @@ mod tests {
162170
to: os("bar"),
163171
format: Format::Ed,
164172
context_count: 3,
173+
report_identical_files: false,
165174
}),
166175
parse_params([os("diff"), os("-e"), os("foo"), os("bar")].iter().cloned())
167176
);
@@ -174,6 +183,7 @@ mod tests {
174183
to: os("bar"),
175184
format: Format::Unified,
176185
context_count: 54,
186+
report_identical_files: false,
177187
}),
178188
parse_params(
179189
[os("diff"), os("-u54"), os("foo"), os("bar")]
@@ -187,6 +197,7 @@ mod tests {
187197
to: os("bar"),
188198
format: Format::Unified,
189199
context_count: 54,
200+
report_identical_files: false,
190201
}),
191202
parse_params(
192203
[os("diff"), os("-U54"), os("foo"), os("bar")]
@@ -200,6 +211,7 @@ mod tests {
200211
to: os("bar"),
201212
format: Format::Unified,
202213
context_count: 54,
214+
report_identical_files: false,
203215
}),
204216
parse_params(
205217
[os("diff"), os("-U"), os("54"), os("foo"), os("bar")]
@@ -213,6 +225,7 @@ mod tests {
213225
to: os("bar"),
214226
format: Format::Context,
215227
context_count: 54,
228+
report_identical_files: false,
216229
}),
217230
parse_params(
218231
[os("diff"), os("-c54"), os("foo"), os("bar")]
@@ -222,13 +235,56 @@ mod tests {
222235
);
223236
}
224237
#[test]
238+
fn report_identical_files() {
239+
assert_eq!(
240+
Ok(Params {
241+
from: os("foo"),
242+
to: os("bar"),
243+
format: Format::Normal,
244+
context_count: 3,
245+
report_identical_files: false,
246+
}),
247+
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
248+
);
249+
assert_eq!(
250+
Ok(Params {
251+
from: os("foo"),
252+
to: os("bar"),
253+
format: Format::Normal,
254+
context_count: 3,
255+
report_identical_files: true,
256+
}),
257+
parse_params([os("diff"), os("-s"), os("foo"), os("bar")].iter().cloned())
258+
);
259+
assert_eq!(
260+
Ok(Params {
261+
from: os("foo"),
262+
to: os("bar"),
263+
format: Format::Normal,
264+
context_count: 3,
265+
report_identical_files: true,
266+
}),
267+
parse_params(
268+
[
269+
os("diff"),
270+
os("--report-identical-files"),
271+
os("foo"),
272+
os("bar"),
273+
]
274+
.iter()
275+
.cloned()
276+
)
277+
);
278+
}
279+
#[test]
225280
fn double_dash() {
226281
assert_eq!(
227282
Ok(Params {
228283
from: os("-g"),
229284
to: os("-h"),
230285
format: Format::Normal,
231286
context_count: 3,
287+
report_identical_files: false,
232288
}),
233289
parse_params([os("diff"), os("--"), os("-g"), os("-h")].iter().cloned())
234290
);

tests/integration.rs

+41
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,47 @@ fn no_differences() -> Result<(), Box<dyn std::error::Error>> {
6262
Ok(())
6363
}
6464

65+
#[test]
66+
fn no_differences_report_identical_files() -> Result<(), Box<dyn std::error::Error>> {
67+
// same file
68+
let mut file1 = NamedTempFile::new()?;
69+
file1.write_all("foo\n".as_bytes())?;
70+
for option in ["", "-u", "-c", "-e"] {
71+
let mut cmd = Command::cargo_bin("diffutils")?;
72+
if !option.is_empty() {
73+
cmd.arg(option);
74+
}
75+
cmd.arg("-s").arg(file1.path()).arg(file1.path());
76+
cmd.assert()
77+
.code(predicate::eq(0))
78+
.success()
79+
.stdout(predicate::eq(format!(
80+
"Files {} and {} are identical\n",
81+
file1.path().to_string_lossy(),
82+
file1.path().to_string_lossy(),
83+
)));
84+
}
85+
// two files with the same content
86+
let mut file2 = NamedTempFile::new()?;
87+
file2.write_all("foo\n".as_bytes())?;
88+
for option in ["", "-u", "-c", "-e"] {
89+
let mut cmd = Command::cargo_bin("diffutils")?;
90+
if !option.is_empty() {
91+
cmd.arg(option);
92+
}
93+
cmd.arg("-s").arg(file1.path()).arg(file2.path());
94+
cmd.assert()
95+
.code(predicate::eq(0))
96+
.success()
97+
.stdout(predicate::eq(format!(
98+
"Files {} and {} are identical\n",
99+
file1.path().to_string_lossy(),
100+
file2.path().to_string_lossy(),
101+
)));
102+
}
103+
Ok(())
104+
}
105+
65106
#[test]
66107
fn differences() -> Result<(), Box<dyn std::error::Error>> {
67108
let mut file1 = NamedTempFile::new()?;

0 commit comments

Comments
 (0)