Skip to content

Commit 043c5f9

Browse files
authored
Merge branch 'main' into context-diff-modification-time
2 parents 9ff8f89 + 0ab824a commit 043c5f9

File tree

13 files changed

+429
-111
lines changed

13 files changed

+429
-111
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ chrono = "0.4.35"
1919
diff = "0.1.10"
2020
regex = "1.10.4"
2121
same-file = "1.0.6"
22+
unicode-width = "0.1.11"
2223

2324
[dev-dependencies]
2425
pretty_assertions = "1"

fuzz/fuzz_targets/fuzz_ed.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::io::Write;
88
use std::process::Command;
99

1010
fn diff_w(expected: &[u8], actual: &[u8], filename: &str) -> Result<Vec<u8>, DiffError> {
11-
let mut output = ed_diff::diff(expected, actual)?;
11+
let mut output = ed_diff::diff(expected, actual, false, false, 8)?;
1212
writeln!(&mut output, "w {filename}").unwrap();
1313
Ok(output)
1414
}

fuzz/fuzz_targets/fuzz_normal.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fuzz_target!(|x: (Vec<u8>, Vec<u8>)| {
2121
} else {
2222
return
2323
}*/
24-
let diff = normal_diff::diff(&from, &to);
24+
let diff = normal_diff::diff(&from, &to, false, false, 8);
2525
File::create("target/fuzz.file.original")
2626
.unwrap()
2727
.write_all(&from)

fuzz/fuzz_targets/fuzz_patch.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ fuzz_target!(|x: (Vec<u8>, Vec<u8>, u8)| {
2626
&to,
2727
"target/fuzz.file",
2828
context as usize,
29+
false,
30+
false,
31+
8,
2932
);
3033
File::create("target/fuzz.file.original")
3134
.unwrap()

src/context_diff.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
use std::collections::VecDeque;
77
use std::io::Write;
88

9+
use crate::utils::do_write_line;
10+
911
#[derive(Debug, PartialEq)]
1012
pub enum DiffLine {
1113
Context(Vec<u8>),
@@ -279,13 +281,16 @@ fn get_modification_time(file_path: &str) -> String {
279281
}
280282

281283
#[must_use]
284+
#[allow(clippy::too_many_arguments)]
282285
pub fn diff(
283286
expected: &[u8],
284287
expected_filename: &str,
285288
actual: &[u8],
286289
actual_filename: &str,
287290
context_size: usize,
288291
stop_early: bool,
292+
expand_tabs: bool,
293+
tabsize: usize,
289294
) -> Vec<u8> {
290295
let expected_file_modified_time = get_modification_time(expected_filename);
291296
let actual_file_modified_time = get_modification_time(actual_filename);
@@ -332,17 +337,20 @@ pub fn diff(
332337
match line {
333338
DiffLine::Context(e) => {
334339
write!(output, " ").expect("write to Vec is infallible");
335-
output.write_all(&e).expect("write to Vec is infallible");
340+
do_write_line(&mut output, &e, expand_tabs, tabsize)
341+
.expect("write to Vec is infallible");
336342
writeln!(output).unwrap();
337343
}
338344
DiffLine::Change(e) => {
339345
write!(output, "! ").expect("write to Vec is infallible");
340-
output.write_all(&e).expect("write to Vec is infallible");
346+
do_write_line(&mut output, &e, expand_tabs, tabsize)
347+
.expect("write to Vec is infallible");
341348
writeln!(output).unwrap();
342349
}
343350
DiffLine::Add(e) => {
344351
write!(output, "- ").expect("write to Vec is infallible");
345-
output.write_all(&e).expect("write to Vec is infallible");
352+
do_write_line(&mut output, &e, expand_tabs, tabsize)
353+
.expect("write to Vec is infallible");
346354
writeln!(output).unwrap();
347355
}
348356
}
@@ -359,17 +367,20 @@ pub fn diff(
359367
match line {
360368
DiffLine::Context(e) => {
361369
write!(output, " ").expect("write to Vec is infallible");
362-
output.write_all(&e).expect("write to Vec is infallible");
370+
do_write_line(&mut output, &e, expand_tabs, tabsize)
371+
.expect("write to Vec is infallible");
363372
writeln!(output).unwrap();
364373
}
365374
DiffLine::Change(e) => {
366375
write!(output, "! ").expect("write to Vec is infallible");
367-
output.write_all(&e).expect("write to Vec is infallible");
376+
do_write_line(&mut output, &e, expand_tabs, tabsize)
377+
.expect("write to Vec is infallible");
368378
writeln!(output).unwrap();
369379
}
370380
DiffLine::Add(e) => {
371381
write!(output, "+ ").expect("write to Vec is infallible");
372-
output.write_all(&e).expect("write to Vec is infallible");
382+
do_write_line(&mut output, &e, expand_tabs, tabsize)
383+
.expect("write to Vec is infallible");
373384
writeln!(output).unwrap();
374385
}
375386
}
@@ -443,6 +454,8 @@ mod tests {
443454
&format!("{target}/alef"),
444455
2,
445456
false,
457+
false,
458+
8,
446459
);
447460
File::create(&format!("{target}/ab.diff"))
448461
.unwrap()
@@ -523,6 +536,8 @@ mod tests {
523536
&format!("{target}/alef_"),
524537
2,
525538
false,
539+
false,
540+
8,
526541
);
527542
File::create(&format!("{target}/ab_.diff"))
528543
.unwrap()
@@ -606,6 +621,8 @@ mod tests {
606621
&format!("{target}/alefx"),
607622
2,
608623
false,
624+
false,
625+
8,
609626
);
610627
File::create(&format!("{target}/abx.diff"))
611628
.unwrap()
@@ -692,6 +709,8 @@ mod tests {
692709
&format!("{target}/alefr"),
693710
2,
694711
false,
712+
false,
713+
8,
695714
);
696715
File::create(&format!("{target}/abr.diff"))
697716
.unwrap()
@@ -730,10 +749,10 @@ mod tests {
730749

731750
let from_filename = "foo";
732751
let _ = File::create(&format!("foo")).unwrap();
733-
let from = vec!["a", "b", "c", ""].join("\n");
752+
let from = ["a", "b", "c", ""].join("\n");
734753
let to_filename = "bar";
735754
let _ = File::create(&format!("bar")).unwrap();
736-
let to = vec!["a", "d", "c", ""].join("\n");
755+
let to = ["a", "d", "c", ""].join("\n");
737756
let context_size: usize = 3;
738757

739758
let diff_full = diff(
@@ -743,13 +762,15 @@ mod tests {
743762
to_filename,
744763
context_size,
745764
false,
765+
false,
766+
8,
746767
);
747768

748769
let diff_full_text = str::from_utf8(&diff_full).unwrap();
749770
let re = Regex::new(r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [+-]\d{4}").unwrap();
750771
let diff_full = re.replace_all(diff_full_text, "");
751772

752-
let expected_full = vec![
773+
let expected_full = [
753774
"*** foo\t",
754775
"--- bar\t",
755776
"***************",
@@ -773,12 +794,14 @@ mod tests {
773794
to_filename,
774795
context_size,
775796
true,
797+
false,
798+
8,
776799
);
777800

778801
let diff_brief_text = str::from_utf8(&diff_brief).unwrap();
779802
let re = Regex::new(r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [+-]\d{4}").unwrap();
780803
let diff_brief = re.replace_all(diff_brief_text, "");
781-
let expected_brief = vec!["*** foo\t", "--- bar\t", ""].join("\n");
804+
let expected_brief = ["*** foo\t", "--- bar\t", ""].join("\n");
782805
assert_eq!(diff_brief, expected_brief);
783806

784807
let nodiff_full = diff(
@@ -788,6 +811,8 @@ mod tests {
788811
to_filename,
789812
context_size,
790813
false,
814+
false,
815+
8,
791816
);
792817
assert!(nodiff_full.is_empty());
793818

@@ -798,6 +823,8 @@ mod tests {
798823
to_filename,
799824
context_size,
800825
true,
826+
false,
827+
8,
801828
);
802829
assert!(nodiff_brief.is_empty());
803830
}

src/ed_diff.rs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
use std::io::Write;
77

8+
use crate::utils::do_write_line;
9+
810
#[derive(Debug, PartialEq)]
911
struct Mismatch {
1012
pub line_number_expected: usize,
@@ -107,7 +109,13 @@ fn make_diff(expected: &[u8], actual: &[u8], stop_early: bool) -> Result<Vec<Mis
107109
Ok(results)
108110
}
109111

110-
pub fn diff(expected: &[u8], actual: &[u8], stop_early: bool) -> Result<Vec<u8>, DiffError> {
112+
pub fn diff(
113+
expected: &[u8],
114+
actual: &[u8],
115+
stop_early: bool,
116+
expand_tabs: bool,
117+
tabsize: usize,
118+
) -> Result<Vec<u8>, DiffError> {
111119
let mut output = Vec::new();
112120
let diff_results = make_diff(expected, actual, stop_early)?;
113121
if stop_early && !diff_results.is_empty() {
@@ -145,7 +153,7 @@ pub fn diff(expected: &[u8], actual: &[u8], stop_early: bool) -> Result<Vec<u8>,
145153
if actual == b"." {
146154
writeln!(&mut output, "..\n.\ns/.//\na").unwrap();
147155
} else {
148-
output.write_all(actual).unwrap();
156+
do_write_line(&mut output, actual, expand_tabs, tabsize).unwrap();
149157
writeln!(&mut output).unwrap();
150158
}
151159
}
@@ -160,7 +168,7 @@ mod tests {
160168
use super::*;
161169
use pretty_assertions::assert_eq;
162170
pub fn diff_w(expected: &[u8], actual: &[u8], filename: &str) -> Result<Vec<u8>, DiffError> {
163-
let mut output = diff(expected, actual, false)?;
171+
let mut output = diff(expected, actual, false, false, 8)?;
164172
writeln!(&mut output, "w {filename}").unwrap();
165173
Ok(output)
166174
}
@@ -169,8 +177,8 @@ mod tests {
169177
fn test_basic() {
170178
let from = b"a\n";
171179
let to = b"b\n";
172-
let diff = diff(from, to, false).unwrap();
173-
let expected = vec!["1c", "b", ".", ""].join("\n");
180+
let diff = diff(from, to, false, false, 8).unwrap();
181+
let expected = ["1c", "b", ".", ""].join("\n");
174182
assert_eq!(diff, expected.as_bytes());
175183
}
176184

@@ -401,21 +409,21 @@ mod tests {
401409

402410
#[test]
403411
fn test_stop_early() {
404-
let from = vec!["a", "b", "c", ""].join("\n");
405-
let to = vec!["a", "d", "c", ""].join("\n");
412+
let from = ["a", "b", "c", ""].join("\n");
413+
let to = ["a", "d", "c", ""].join("\n");
406414

407-
let diff_full = diff(from.as_bytes(), to.as_bytes(), false).unwrap();
408-
let expected_full = vec!["2c", "d", ".", ""].join("\n");
415+
let diff_full = diff(from.as_bytes(), to.as_bytes(), false, false, 8).unwrap();
416+
let expected_full = ["2c", "d", ".", ""].join("\n");
409417
assert_eq!(diff_full, expected_full.as_bytes());
410418

411-
let diff_brief = diff(from.as_bytes(), to.as_bytes(), true).unwrap();
419+
let diff_brief = diff(from.as_bytes(), to.as_bytes(), true, false, 8).unwrap();
412420
let expected_brief = "\0".as_bytes();
413421
assert_eq!(diff_brief, expected_brief);
414422

415-
let nodiff_full = diff(from.as_bytes(), from.as_bytes(), false).unwrap();
423+
let nodiff_full = diff(from.as_bytes(), from.as_bytes(), false, false, 8).unwrap();
416424
assert!(nodiff_full.is_empty());
417425

418-
let nodiff_brief = diff(from.as_bytes(), from.as_bytes(), true).unwrap();
426+
let nodiff_brief = diff(from.as_bytes(), from.as_bytes(), true, false, 8).unwrap();
419427
assert!(nodiff_brief.is_empty());
420428
}
421429
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod context_diff;
22
pub mod ed_diff;
33
pub mod normal_diff;
44
pub mod unified_diff;
5+
pub mod utils;
56

67
// Re-export the public functions/types you need
78
pub use context_diff::diff as context_diff;

src/main.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod ed_diff;
1515
mod normal_diff;
1616
mod params;
1717
mod unified_diff;
18+
mod utils;
1819

1920
// Exit codes are documented at
2021
// https://www.gnu.org/software/diffutils/manual/html_node/Invoking-diff.html.
@@ -30,6 +31,8 @@ fn main() -> ExitCode {
3031
format,
3132
report_identical_files,
3233
brief,
34+
expand_tabs,
35+
tabsize,
3336
} = parse_params(opts).unwrap_or_else(|error| {
3437
eprintln!("{error}");
3538
exit(2);
@@ -65,14 +68,18 @@ fn main() -> ExitCode {
6568
};
6669
// run diff
6770
let result: Vec<u8> = match format {
68-
Format::Normal => normal_diff::diff(&from_content, &to_content, brief),
71+
Format::Normal => {
72+
normal_diff::diff(&from_content, &to_content, brief, expand_tabs, tabsize)
73+
}
6974
Format::Unified => unified_diff::diff(
7075
&from_content,
7176
&from.to_string_lossy(),
7277
&to_content,
7378
&to.to_string_lossy(),
7479
context_count,
7580
brief,
81+
expand_tabs,
82+
tabsize,
7683
),
7784
Format::Context => context_diff::diff(
7885
&from_content,
@@ -81,11 +88,14 @@ fn main() -> ExitCode {
8188
&to.to_string_lossy(),
8289
context_count,
8390
brief,
91+
expand_tabs,
92+
tabsize,
8493
),
85-
Format::Ed => ed_diff::diff(&from_content, &to_content, brief).unwrap_or_else(|error| {
86-
eprintln!("{error}");
87-
exit(2);
88-
}),
94+
Format::Ed => ed_diff::diff(&from_content, &to_content, brief, expand_tabs, tabsize)
95+
.unwrap_or_else(|error| {
96+
eprintln!("{error}");
97+
exit(2);
98+
}),
8999
};
90100
if brief && !result.is_empty() {
91101
println!(

0 commit comments

Comments
 (0)