Skip to content

Create the side-by-side option (-y) feature for the diff command (Incomplete) #117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 3, 2025
Merged
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
1 change: 1 addition & 0 deletions .github/workflows/fuzzing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
- { name: fuzz_ed, should_pass: true }
- { name: fuzz_normal, should_pass: true }
- { name: fuzz_patch, should_pass: true }
- { name: fuzz_side, should_pass: true }
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
Expand Down
6 changes: 5 additions & 1 deletion fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ path = "fuzz_targets/fuzz_ed.rs"
test = false
doc = false


[[bin]]
name = "fuzz_side"
path = "fuzz_targets/fuzz_side.rs"
test = false
doc = false
42 changes: 42 additions & 0 deletions fuzz/fuzz_targets/fuzz_side.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#![no_main]
#[macro_use]
extern crate libfuzzer_sys;

use diffutilslib::side_diff;

use std::fs::File;
use std::io::Write;
use diffutilslib::params::Params;

fuzz_target!(|x: (Vec<u8>, Vec<u8>, /* usize, usize */ bool)| {
let (original, new, /* width, tabsize, */ expand) = x;

// if width == 0 || tabsize == 0 {
// return;
// }

let params = Params {
// width,
// tabsize,
expand_tabs: expand,
..Default::default()
};
let mut output_buf = vec![];
side_diff::diff(&original, &new, &mut output_buf, &params);
File::create("target/fuzz.file.original")
.unwrap()
.write_all(&original)
.unwrap();
File::create("target/fuzz.file.new")
.unwrap()
.write_all(&new)
.unwrap();
File::create("target/fuzz.file")
.unwrap()
.write_all(&original)
.unwrap();
File::create("target/fuzz.diff")
.unwrap()
.write_all(&output_buf)
.unwrap();
Comment on lines +26 to +41
Copy link
Preview

Copilot AI May 27, 2025

Choose a reason for hiding this comment

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

[nitpick] Consider removing disk writes from the fuzz target to avoid unnecessary I/O overhead and side effects during fuzzing. Using in-memory buffers for debugging outputs can improve iteration speed.

Suggested change
File::create("target/fuzz.file.original")
.unwrap()
.write_all(&original)
.unwrap();
File::create("target/fuzz.file.new")
.unwrap()
.write_all(&new)
.unwrap();
File::create("target/fuzz.file")
.unwrap()
.write_all(&original)
.unwrap();
File::create("target/fuzz.diff")
.unwrap()
.write_all(&output_buf)
.unwrap();
let mut debug_original = Vec::new();
debug_original.extend_from_slice(&original);
let mut debug_new = Vec::new();
debug_new.extend_from_slice(&new);
let mut debug_diff = Vec::new();
debug_diff.extend_from_slice(&output_buf);

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wouldn't mind changing it, but it would be out of standard compared to other files related to fuzz.

});
8 changes: 6 additions & 2 deletions src/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

use crate::params::{parse_params, Format};
use crate::utils::report_failure_to_read_input_file;
use crate::{context_diff, ed_diff, normal_diff, unified_diff};
use crate::{context_diff, ed_diff, normal_diff, side_diff, unified_diff};
use std::env::ArgsOs;
use std::ffi::OsString;
use std::fs;
use std::io::{self, Read, Write};
use std::io::{self, stdout, Read, Write};
use std::iter::Peekable;
use std::process::{exit, ExitCode};

Expand Down Expand Up @@ -79,6 +79,10 @@
eprintln!("{error}");
exit(2);
}),
Format::SideBySide => {
let mut output = stdout().lock();
side_diff::diff(&from_content, &to_content, &mut output, &params)

Check warning on line 84 in src/diff.rs

View check run for this annotation

Codecov / codecov/patch

src/diff.rs#L83-L84

Added lines #L83 - L84 were not covered by tests
}
};
if params.brief && !result.is_empty() {
println!(
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ pub mod ed_diff;
pub mod macros;
pub mod normal_diff;
pub mod params;
pub mod side_diff;
pub mod unified_diff;
pub mod utils;

// Re-export the public functions/types you need
pub use context_diff::diff as context_diff;
pub use ed_diff::diff as ed_diff;
pub use normal_diff::diff as normal_diff;
pub use side_diff::diff as side_by_side_diff;
pub use unified_diff::diff as unified_diff;
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod ed_diff;
mod macros;
mod normal_diff;
mod params;
mod side_diff;
mod unified_diff;
mod utils;

Expand Down
45 changes: 42 additions & 3 deletions src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
Unified,
Context,
Ed,
SideBySide,
}

#[derive(Clone, Debug, Eq, PartialEq)]
Expand All @@ -24,6 +25,7 @@
pub brief: bool,
pub expand_tabs: bool,
pub tabsize: usize,
pub width: usize,
}

impl Default for Params {
Expand All @@ -38,6 +40,7 @@
brief: false,
expand_tabs: false,
tabsize: 8,
width: 130,
}
}
}
Expand All @@ -57,6 +60,7 @@
let mut format = None;
let mut context = None;
let tabsize_re = Regex::new(r"^--tabsize=(?<num>\d+)$").unwrap();
let width_re = Regex::new(r"--width=(?P<long>\d+)$").unwrap();
while let Some(param) = opts.next() {
let next_param = opts.peek();
if param == "--" {
Expand Down Expand Up @@ -101,6 +105,34 @@
format = Some(Format::Ed);
continue;
}
if param == "-y" || param == "--side-by-side" {
if format.is_some() && format != Some(Format::SideBySide) {
return Err("Conflicting output style option".to_string());
}
format = Some(Format::SideBySide);
continue;

Check warning on line 113 in src/params.rs

View check run for this annotation

Codecov / codecov/patch

src/params.rs#L110-L113

Added lines #L110 - L113 were not covered by tests
}
if width_re.is_match(param.to_string_lossy().as_ref()) {
let param = param.into_string().unwrap();
let width_str: &str = width_re
.captures(param.as_str())
.unwrap()
.name("long")
.unwrap()
.as_str();

Check warning on line 122 in src/params.rs

View check run for this annotation

Codecov / codecov/patch

src/params.rs#L116-L122

Added lines #L116 - L122 were not covered by tests

params.width = match width_str.parse::<usize>() {
Ok(num) => {

Check warning on line 125 in src/params.rs

View check run for this annotation

Codecov / codecov/patch

src/params.rs#L124-L125

Added lines #L124 - L125 were not covered by tests
if num == 0 {
return Err("invalid width «0»".to_string());
}

Check warning on line 128 in src/params.rs

View check run for this annotation

Codecov / codecov/patch

src/params.rs#L127-L128

Added lines #L127 - L128 were not covered by tests

num

Check warning on line 130 in src/params.rs

View check run for this annotation

Codecov / codecov/patch

src/params.rs#L130

Added line #L130 was not covered by tests
}
Err(_) => return Err(format!("invalid width «{width_str}»")),

Check warning on line 132 in src/params.rs

View check run for this annotation

Codecov / codecov/patch

src/params.rs#L132

Added line #L132 was not covered by tests
};
continue;

Check warning on line 134 in src/params.rs

View check run for this annotation

Codecov / codecov/patch

src/params.rs#L134

Added line #L134 was not covered by tests
}
if tabsize_re.is_match(param.to_string_lossy().as_ref()) {
// Because param matches the regular expression,
// it is safe to assume it is valid UTF-8.
Expand All @@ -112,9 +144,16 @@
.unwrap()
.as_str();
params.tabsize = match tabsize_str.parse::<usize>() {
Ok(num) => num,
Ok(num) => {
if num == 0 {
return Err("invalid tabsize «0»".to_string());

Check warning on line 149 in src/params.rs

View check run for this annotation

Codecov / codecov/patch

src/params.rs#L149

Added line #L149 was not covered by tests
}

num
}
Err(_) => return Err(format!("invalid tabsize «{tabsize_str}»")),
};

continue;
}
match match_context_diff_params(&param, next_param, format) {
Expand Down Expand Up @@ -704,11 +743,11 @@
executable: os("diff"),
from: os("foo"),
to: os("bar"),
tabsize: 0,
tabsize: 1,
..Default::default()
}),
parse_params(
[os("diff"), os("--tabsize=0"), os("foo"), os("bar")]
[os("diff"), os("--tabsize=1"), os("foo"), os("bar")]
.iter()
.cloned()
.peekable()
Expand Down
Loading
Loading