diff --git a/src/params.rs b/src/params.rs index 6a6d261..3c1bb36 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,168 @@ mod tests { ); } #[test] + fn unified_valid() { + assert_eq!( + Ok(Params { + from: os("foo"), + to: os("bar"), + format: Format::Unified, + ..Default::default() + }), + parse_params([os("diff"), os("-u"), os("foo"), os("bar")].iter().cloned()) + ); + assert_eq!( + Ok(Params { + from: os("foo"), + to: os("bar"), + format: Format::Unified, + context_count: 42, + ..Default::default() + }), + parse_params( + [os("diff"), os("-u42"), os("foo"), os("bar")] + .iter() + .cloned() + ) + ); + assert_eq!( + Ok(Params { + from: os("foo"), + to: os("bar"), + format: Format::Unified, + context_count: 42, + ..Default::default() + }), + parse_params( + [os("diff"), os("-U42"), os("foo"), os("bar")] + .iter() + .cloned() + ) + ); + assert_eq!( + Ok(Params { + from: os("foo"), + to: os("bar"), + format: Format::Unified, + context_count: 42, + ..Default::default() + }), + parse_params( + [os("diff"), os("-U"), os("42"), os("foo"), os("bar")] + .iter() + .cloned() + ) + ); + assert_eq!( + Ok(Params { + from: os("foo"), + to: os("bar"), + format: Format::Unified, + ..Default::default() + }), + parse_params( + [os("diff"), os("--unified"), os("foo"), os("bar")] + .iter() + .cloned() + ) + ); + assert_eq!( + Ok(Params { + from: os("foo"), + to: os("bar"), + format: Format::Unified, + ..Default::default() + }), + parse_params( + [os("diff"), os("--unified="), os("foo"), os("bar")] + .iter() + .cloned() + ) + ); + assert_eq!( + Ok(Params { + from: os("foo"), + to: os("bar"), + format: Format::Unified, + context_count: 42, + ..Default::default() + }), + parse_params( + [os("diff"), os("--unified=42"), os("foo"), os("bar")] + .iter() + .cloned() + ) + ); + assert_eq!( + Ok(Params { + from: os("foo"), + to: os("bar"), + format: Format::Unified, + context_count: 42, + ..Default::default() + }), + parse_params( + [os("diff"), os("-42u"), os("foo"), os("bar")] + .iter() + .cloned() + ) + ); + } + #[test] + fn unified_invalid() { + assert!(parse_params( + [os("diff"), os("-u"), os("42"), os("foo"), os("bar")] + .iter() + .cloned() + ) + .is_err()); + assert!(parse_params( + [os("diff"), os("-u=42"), os("foo"), os("bar")] + .iter() + .cloned() + ) + .is_err()); + assert!(parse_params( + [os("diff"), os("-u="), os("foo"), os("bar")] + .iter() + .cloned() + ) + .is_err()); + assert!( + parse_params([os("diff"), os("-U"), os("foo"), os("bar")].iter().cloned()).is_err() + ); + assert!(parse_params( + [os("diff"), os("-U=42"), os("foo"), os("bar")] + .iter() + .cloned() + ) + .is_err()); + assert!(parse_params( + [os("diff"), os("-U="), os("foo"), os("bar")] + .iter() + .cloned() + ) + .is_err()); + assert!(parse_params( + [os("diff"), os("--unified42"), os("foo"), os("bar")] + .iter() + .cloned() + ) + .is_err()); + assert!(parse_params( + [os("diff"), os("--unified"), os("42"), os("foo"), os("bar")] + .iter() + .cloned() + ) + .is_err()); + assert!(parse_params( + [os("diff"), os("-42U"), os("foo"), os("bar")] + .iter() + .cloned() + ) + .is_err()); + } + #[test] fn context_count() { assert_eq!( Ok(Params {