diff --git a/src/params.rs b/src/params.rs index 7e9cc78..c0fdd32 100644 --- a/src/params.rs +++ b/src/params.rs @@ -50,7 +50,7 @@ impl Default for Params { } pub fn parse_params>(opts: I) -> Result { - let mut opts = opts.into_iter(); + let mut opts = opts.into_iter().peekable(); // parse CLI let Some(exe) = opts.next() else { @@ -60,7 +60,10 @@ pub fn parse_params>(opts: I) -> Result\d+)$").unwrap(); + let unified_re = + Regex::new(r"^(-[uU](?\d*)|--unified(=(?\d*))?|-(?\d+)u)$").unwrap(); while let Some(param) = opts.next() { if param == "--" { break; @@ -103,6 +106,40 @@ pub fn parse_params>(opts: I) -> Result().unwrap()); + } + if param == "-U" { + let next_param = opts.peek(); + if next_param.is_some() { + let next_value = next_param + .unwrap() + .to_string_lossy() + .as_ref() + .parse::(); + if next_value.is_ok() { + context_count = Some(next_value.unwrap()); + opts.next(); + } else { + return Err(format!( + "invalid context length '{}'", + next_param.unwrap().to_string_lossy() + )); + } + } + } + continue; + } let p = osstr_bytes(¶m); if p.first() == Some(&b'-') && p.get(1) != Some(&b'-') { let mut bit = p[1..].iter().copied().peekable(); @@ -111,10 +148,12 @@ pub fn parse_params>(opts: I) -> Result { - params.context_count = (b - b'0') as usize; + context_count = Some((b - b'0') as usize); while let Some(b'0'..=b'9') = bit.peek() { - params.context_count *= 10; - params.context_count += (bit.next().unwrap() - b'0') as usize; + context_count = Some(context_count.unwrap() * 10); + context_count = Some( + context_count.unwrap() + (bit.next().unwrap() - b'0') as usize, + ); } } b'c' => { @@ -129,30 +168,6 @@ pub fn parse_params>(opts: I) -> Result { - if format.is_some() && format != Some(Format::Unified) { - return Err("Conflicting output style options".to_string()); - } - format = Some(Format::Unified); - } - b'U' => { - if format.is_some() && format != Some(Format::Unified) { - return Err("Conflicting output style options".to_string()); - } - format = Some(Format::Unified); - let context_count_maybe = if bit.peek().is_some() { - String::from_utf8(bit.collect::>()).ok() - } else { - opts.next().map(|x| x.to_string_lossy().into_owned()) - }; - if let Some(context_count_maybe) = - context_count_maybe.and_then(|x| x.parse().ok()) - { - params.context_count = context_count_maybe; - break; - } - return Err("Invalid context count".to_string()); - } _ => return Err(format!("Unknown option: {}", String::from_utf8_lossy(&[b]))), } } @@ -179,6 +194,9 @@ pub fn parse_params>(opts: I) -> Result ", exe.to_string_lossy())); }; params.format = format.unwrap_or(Format::default()); + if context_count.is_some() { + params.context_count = context_count.unwrap(); + } Ok(params) } @@ -212,6 +230,63 @@ mod tests { ); } #[test] + fn unified_valid() { + for args in [vec!["-u"], vec!["--unified"], vec!["--unified="]] { + let mut params = vec!["diff"]; + params.extend(args); + params.extend(["foo", "bar"]); + assert_eq!( + Ok(Params { + from: os("foo"), + to: os("bar"), + format: Format::Unified, + ..Default::default() + }), + parse_params(params.iter().map(|x| os(x))) + ); + } + for args in [ + vec!["-u42"], + vec!["-U42"], + vec!["-U", "42"], + vec!["--unified=42"], + vec!["-42u"], + ] { + let mut params = vec!["diff"]; + params.extend(args); + params.extend(["foo", "bar"]); + assert_eq!( + Ok(Params { + from: os("foo"), + to: os("bar"), + format: Format::Unified, + context_count: 42, + ..Default::default() + }), + parse_params(params.iter().map(|x| os(x))) + ); + } + } + #[test] + fn unified_invalid() { + for args in [ + vec!["-u", "42"], + vec!["-u=42"], + vec!["-u="], + vec!["-U"], + vec!["-U=42"], + vec!["-U="], + vec!["--unified42"], + vec!["--unified", "42"], + vec!["-42U"], + ] { + let mut params = vec!["diff"]; + params.extend(args); + params.extend(["foo", "bar"]); + assert!(parse_params(params.iter().map(|x| os(x))).is_err()); + } + } + #[test] fn context_count() { assert_eq!( Ok(Params {