Skip to content

Commit 22d973f

Browse files
committed
Parse all valid arguments accepted by GNU diff to request a regular context (with an optional number of lines)
1 parent fe28610 commit 22d973f

File tree

1 file changed

+93
-17
lines changed

1 file changed

+93
-17
lines changed

src/params.rs

Lines changed: 93 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
6262
let mut format = None;
6363
let mut context_count = None;
6464
let tabsize_re = Regex::new(r"^--tabsize=(?<num>\d+)$").unwrap();
65+
let context_re =
66+
Regex::new(r"^(-[cC](?<num1>\d*)|--context(=(?<num2>\d*))?|-(?<num3>\d+)c)$").unwrap();
6567
let unified_re =
6668
Regex::new(r"^(-[uU](?<num1>\d*)|--unified(=(?<num2>\d*))?|-(?<num3>\d+)u)$").unwrap();
6769
while let Some(param) = opts.next() {
@@ -106,6 +108,40 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
106108
};
107109
continue;
108110
}
111+
if context_re.is_match(param.to_string_lossy().as_ref()) {
112+
if format.is_some() && format != Some(Format::Context) {
113+
return Err("Conflicting output style options".to_string());
114+
}
115+
format = Some(Format::Context);
116+
let captures = context_re.captures(param.to_str().unwrap()).unwrap();
117+
let num = captures
118+
.name("num1")
119+
.or(captures.name("num2"))
120+
.or(captures.name("num3"));
121+
if num.is_some() && !num.unwrap().as_str().is_empty() {
122+
context_count = Some(num.unwrap().as_str().parse::<usize>().unwrap());
123+
}
124+
if param == "-C" {
125+
let next_param = opts.peek();
126+
if next_param.is_some() {
127+
let next_value = next_param
128+
.unwrap()
129+
.to_string_lossy()
130+
.as_ref()
131+
.parse::<usize>();
132+
if next_value.is_ok() {
133+
context_count = Some(next_value.unwrap());
134+
opts.next();
135+
} else {
136+
return Err(format!(
137+
"invalid context length '{}'",
138+
next_param.unwrap().to_string_lossy()
139+
));
140+
}
141+
}
142+
}
143+
continue;
144+
}
109145
if unified_re.is_match(param.to_string_lossy().as_ref()) {
110146
if format.is_some() && format != Some(Format::Unified) {
111147
return Err("Conflicting output style options".to_string());
@@ -143,25 +179,8 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
143179
let p = osstr_bytes(&param);
144180
if p.first() == Some(&b'-') && p.get(1) != Some(&b'-') {
145181
let mut bit = p[1..].iter().copied().peekable();
146-
// Can't use a for loop because `diff -30u` is supposed to make a diff
147-
// with 30 lines of context.
148182
while let Some(b) = bit.next() {
149183
match b {
150-
b'0'..=b'9' => {
151-
context_count = Some((b - b'0') as usize);
152-
while let Some(b'0'..=b'9') = bit.peek() {
153-
context_count = Some(context_count.unwrap() * 10);
154-
context_count = Some(
155-
context_count.unwrap() + (bit.next().unwrap() - b'0') as usize,
156-
);
157-
}
158-
}
159-
b'c' => {
160-
if format.is_some() && format != Some(Format::Context) {
161-
return Err("Conflicting output style options".to_string());
162-
}
163-
format = Some(Format::Context);
164-
}
165184
b'e' => {
166185
if format.is_some() && format != Some(Format::Ed) {
167186
return Err("Conflicting output style options".to_string());
@@ -230,6 +249,63 @@ mod tests {
230249
);
231250
}
232251
#[test]
252+
fn context_valid() {
253+
for args in [vec!["-c"], vec!["--context"], vec!["--context="]] {
254+
let mut params = vec!["diff"];
255+
params.extend(args);
256+
params.extend(["foo", "bar"]);
257+
assert_eq!(
258+
Ok(Params {
259+
from: os("foo"),
260+
to: os("bar"),
261+
format: Format::Context,
262+
..Default::default()
263+
}),
264+
parse_params(params.iter().map(|x| os(x)))
265+
);
266+
}
267+
for args in [
268+
vec!["-c42"],
269+
vec!["-C42"],
270+
vec!["-C", "42"],
271+
vec!["--context=42"],
272+
vec!["-42c"],
273+
] {
274+
let mut params = vec!["diff"];
275+
params.extend(args);
276+
params.extend(["foo", "bar"]);
277+
assert_eq!(
278+
Ok(Params {
279+
from: os("foo"),
280+
to: os("bar"),
281+
format: Format::Context,
282+
context_count: 42,
283+
..Default::default()
284+
}),
285+
parse_params(params.iter().map(|x| os(x)))
286+
);
287+
}
288+
}
289+
#[test]
290+
fn context_invalid() {
291+
for args in [
292+
vec!["-c", "42"],
293+
vec!["-c=42"],
294+
vec!["-c="],
295+
vec!["-C"],
296+
vec!["-C=42"],
297+
vec!["-C="],
298+
vec!["--context42"],
299+
vec!["--context", "42"],
300+
vec!["-42C"],
301+
] {
302+
let mut params = vec!["diff"];
303+
params.extend(args);
304+
params.extend(["foo", "bar"]);
305+
assert!(parse_params(params.iter().map(|x| os(x))).is_err());
306+
}
307+
}
308+
#[test]
233309
fn unified_valid() {
234310
for args in [vec!["-u"], vec!["--unified"], vec!["--unified="]] {
235311
let mut params = vec!["diff"];

0 commit comments

Comments
 (0)