Skip to content

Commit d50d5ad

Browse files
committed
diff: simplify the parsing of short option clusters
1 parent b53b92a commit d50d5ad

2 files changed

Lines changed: 61 additions & 92 deletions

File tree

‎src/params.rs‎

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -79,35 +79,17 @@ pub fn parse_params<I: Iterator<Item = OsString>>(mut opts: Peekable<I>) -> Resu
7979
}
8080
continue;
8181
}
82+
// A word of short options taking no argument, such as `-qst`. Other
83+
// words are left whole for legacy numeric options and their diagnostics.
8284
if let Some(short) = param
8385
.to_str()
8486
.and_then(|arg| arg.strip_prefix('-'))
85-
.filter(|arg| !arg.starts_with('-'))
87+
.filter(|short| !short.is_empty() && short.bytes().all(|b| SHORT_OPTIONS.contains(&b)))
8688
{
87-
if short.len() == 1 {
88-
if apply_short_option(short.as_bytes()[0], &mut params, &mut format)? {
89-
continue;
90-
}
91-
} else if short.len() > 1 {
92-
// Leave mixed words whole for legacy numeric options and their diagnostics.
93-
let all_recognized = short.bytes().all(|option| {
94-
let mut isolated_format = None;
95-
matches!(
96-
apply_short_option(option, &mut Params::default(), &mut isolated_format),
97-
Ok(true)
98-
)
99-
});
100-
if all_recognized {
101-
let mut clustered_params = params.clone();
102-
let mut clustered_format = format;
103-
for option in short.bytes() {
104-
apply_short_option(option, &mut clustered_params, &mut clustered_format)?;
105-
}
106-
params = clustered_params;
107-
format = clustered_format;
108-
continue;
109-
}
89+
for option in short.bytes() {
90+
apply_short_option(option, &mut params, &mut format)?;
11091
}
92+
continue;
11193
}
11294
if param == "--report-identical-files" {
11395
params.report_identical_files = true;
@@ -282,11 +264,14 @@ fn set_format(format: &mut Option<Format>, requested: Format) -> Result<(), Stri
282264
Ok(())
283265
}
284266

267+
/// The short options that take no argument, and can be grouped.
268+
const SHORT_OPTIONS: &[u8] = b"ceqstuy";
269+
285270
fn apply_short_option(
286271
option: u8,
287272
params: &mut Params,
288273
format: &mut Option<Format>,
289-
) -> Result<bool, String> {
274+
) -> Result<(), String> {
290275
match option {
291276
b'c' => set_format(format, Format::Context)?,
292277
b'e' => set_format(format, Format::Ed)?,
@@ -295,9 +280,10 @@ fn apply_short_option(
295280
b't' => params.expand_tabs = true,
296281
b'u' => set_format(format, Format::Unified)?,
297282
b'y' => set_format(format, Format::SideBySide)?,
298-
_ => return Ok(false),
283+
// Callers only pass bytes of SHORT_OPTIONS.
284+
_ => unreachable!("not a short option: {}", char::from(option)),
299285
}
300-
Ok(true)
286+
Ok(())
301287
}
302288

303289
struct DiffStyleMatch {
@@ -803,7 +789,7 @@ mod tests {
803789
}
804790
}
805791
#[test]
806-
fn control_partly_recognized_short_option_clusters() {
792+
fn partly_recognized_short_option_clusters() {
807793
for options in [
808794
vec!["-qg"],
809795
vec!["-u", "-cX"],

‎tests/by-util/test_diff.rs‎

Lines changed: 47 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -119,76 +119,59 @@ fn differences_brief() -> Result<(), Box<dyn std::error::Error>> {
119119
}
120120

121121
#[test]
122-
fn short_option_clusters_match_split_options() -> Result<(), Box<dyn std::error::Error>> {
122+
fn short_option_clusters() -> Result<(), Box<dyn std::error::Error>> {
123123
let mut left = NamedTempFile::new()?;
124-
left.write_all(b"a\tb\n")?;
124+
left.write_all(b"one\ttwo\n")?;
125125
let mut right = NamedTempFile::new()?;
126-
right.write_all(b"a b\n")?;
127-
128-
let equivalent = |cluster: &[&str],
129-
split: &[&str],
130-
other: &std::path::Path|
131-
-> Result<std::process::Output, Box<dyn std::error::Error>> {
132-
let run = |options: &[&str]| {
133-
cargo_bin_cmd!("diffutils")
134-
.arg("diff")
135-
.args(options)
136-
.arg(left.path())
137-
.arg(other)
138-
.output()
139-
};
140-
let clustered = run(cluster)?;
141-
let separate = run(split)?;
142-
assert_eq!(
143-
clustered.status.code(),
144-
separate.status.code(),
145-
"{cluster:?}"
146-
);
147-
assert_eq!(clustered.stdout, separate.stdout, "{cluster:?}");
148-
assert_eq!(clustered.stderr, separate.stderr, "{cluster:?}");
149-
Ok(clustered)
150-
};
151-
152-
for (cluster, split, expected_code) in [
153-
(&["-cc"][..], &["-c", "-c"][..], Some(1)),
154-
(&["-ee"], &["-e", "-e"], Some(1)),
155-
(&["-qq"], &["-q", "-q"], Some(1)),
156-
(&["-ss"], &["-s", "-s"], Some(1)),
157-
(&["-tt"], &["-t", "-t"], Some(1)),
158-
(&["-uu"], &["-u", "-u"], Some(1)),
159-
(&["-cqt"], &["-c", "-q", "-t"], Some(1)),
160-
(&["-eqs"], &["-e", "-q", "-s"], Some(1)),
161-
(&["-qst"], &["-q", "-s", "-t"], Some(1)),
162-
(&["-uqt"], &["-u", "-q", "-t"], Some(1)),
163-
(&["-cu"], &["-c", "-u"], Some(2)),
164-
(&["-uc"], &["-u", "-c"], Some(2)),
165-
(&["-eu"], &["-e", "-u"], Some(2)),
166-
(&["-c", "-ut"], &["-c", "-u", "-t"], Some(2)),
167-
(&["-ut", "-c"], &["-u", "-t", "-c"], Some(2)),
168-
] {
169-
let clustered = equivalent(cluster, split, right.path())?;
170-
assert_eq!(clustered.status.code(), expected_code, "{cluster:?}");
171-
}
172-
126+
right.write_all(b"one two\n")?;
173127
let mut same = NamedTempFile::new()?;
174-
same.write_all(b"a\tb\n")?;
175-
let identical = format!(
128+
same.write_all(b"one\ttwo\n")?;
129+
130+
let mut cmd = cargo_bin_cmd!("diffutils");
131+
cmd.arg("diff")
132+
.arg("-qs")
133+
.arg(left.path())
134+
.arg(right.path());
135+
cmd.assert().code(predicate::eq(1)).stdout(format!(
136+
"Files {} and {} differ\n",
137+
left.path().to_string_lossy(),
138+
right.path().to_string_lossy()
139+
));
140+
141+
let mut cmd = cargo_bin_cmd!("diffutils");
142+
cmd.arg("diff").arg("-sq").arg(left.path()).arg(same.path());
143+
cmd.assert().code(predicate::eq(0)).stdout(format!(
176144
"Files {} and {} are identical\n",
177145
left.path().to_string_lossy(),
178146
same.path().to_string_lossy()
179-
);
180-
for (cluster, split) in [
181-
(&["-ss"][..], &["-s", "-s"][..]),
182-
(&["-qs"], &["-q", "-s"]),
183-
(&["-ys"], &["-y", "-s"]),
184-
] {
185-
let clustered = equivalent(cluster, split, same.path())?;
186-
assert_eq!(clustered.status.code(), Some(0), "{cluster:?}");
187-
assert!(
188-
clustered.stdout.ends_with(identical.as_bytes()),
189-
"{cluster:?}"
190-
);
191-
}
147+
));
148+
149+
let mut cmd = cargo_bin_cmd!("diffutils");
150+
cmd.arg("diff")
151+
.arg("-te")
152+
.arg(right.path())
153+
.arg(left.path());
154+
cmd.assert()
155+
.code(predicate::eq(1))
156+
.stdout("1c\none two\n.\n");
157+
158+
let mut cmd = cargo_bin_cmd!("diffutils");
159+
cmd.arg("diff")
160+
.arg("-ue")
161+
.arg(left.path())
162+
.arg(right.path());
163+
cmd.assert()
164+
.code(predicate::eq(2))
165+
.stderr(predicate::str::contains("Conflicting output style options"));
166+
167+
let mut cmd = cargo_bin_cmd!("diffutils");
168+
cmd.arg("diff")
169+
.arg("-qz")
170+
.arg(left.path())
171+
.arg(right.path());
172+
cmd.assert()
173+
.code(predicate::eq(2))
174+
.stderr(predicate::str::contains("unrecognized option '-qz'"));
192175
Ok(())
193176
}
194177

0 commit comments

Comments
 (0)