@@ -50,7 +50,7 @@ impl Default for Params {
5050}
5151
5252pub fn parse_params < I : IntoIterator < Item = OsString > > ( opts : I ) -> Result < Params , String > {
53- let mut opts = opts. into_iter ( ) ;
53+ let mut opts = opts. into_iter ( ) . peekable ( ) ;
5454 // parse CLI
5555
5656 let Some ( exe) = opts. next ( ) else {
@@ -60,7 +60,10 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
6060 let mut from = None ;
6161 let mut to = None ;
6262 let mut format = None ;
63+ let mut context_count = None ;
6364 let tabsize_re = Regex :: new ( r"^--tabsize=(?<num>\d+)$" ) . unwrap ( ) ;
65+ let unified_re =
66+ Regex :: new ( r"^(-[uU](?<num1>\d*)|--unified(=(?<num2>\d*))?|-(?<num3>\d+)u)$" ) . unwrap ( ) ;
6467 while let Some ( param) = opts. next ( ) {
6568 if param == "--" {
6669 break ;
@@ -103,6 +106,40 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
103106 } ;
104107 continue ;
105108 }
109+ if unified_re. is_match ( param. to_string_lossy ( ) . as_ref ( ) ) {
110+ if format. is_some ( ) && format != Some ( Format :: Unified ) {
111+ return Err ( "Conflicting output style options" . to_string ( ) ) ;
112+ }
113+ format = Some ( Format :: Unified ) ;
114+ let captures = unified_re. captures ( param. to_str ( ) . unwrap ( ) ) . unwrap ( ) ;
115+ let num = captures
116+ . name ( "num1" )
117+ . or ( captures. name ( "num2" ) )
118+ . or ( captures. name ( "num3" ) ) ;
119+ if num. is_some ( ) && !num. unwrap ( ) . as_str ( ) . is_empty ( ) {
120+ context_count = Some ( num. unwrap ( ) . as_str ( ) . parse :: < usize > ( ) . unwrap ( ) ) ;
121+ }
122+ if param == "-U" {
123+ let next_param = opts. peek ( ) ;
124+ if next_param. is_some ( ) {
125+ let next_value = next_param
126+ . unwrap ( )
127+ . to_string_lossy ( )
128+ . as_ref ( )
129+ . parse :: < usize > ( ) ;
130+ if next_value. is_ok ( ) {
131+ context_count = Some ( next_value. unwrap ( ) ) ;
132+ opts. next ( ) ;
133+ } else {
134+ return Err ( format ! (
135+ "invalid context length '{}'" ,
136+ next_param. unwrap( ) . to_string_lossy( )
137+ ) ) ;
138+ }
139+ }
140+ }
141+ continue ;
142+ }
106143 let p = osstr_bytes ( & param) ;
107144 if p. first ( ) == Some ( & b'-' ) && p. get ( 1 ) != Some ( & b'-' ) {
108145 let mut bit = p[ 1 ..] . iter ( ) . copied ( ) . peekable ( ) ;
@@ -111,10 +148,12 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
111148 while let Some ( b) = bit. next ( ) {
112149 match b {
113150 b'0' ..=b'9' => {
114- params . context_count = ( b - b'0' ) as usize ;
151+ context_count = Some ( ( b - b'0' ) as usize ) ;
115152 while let Some ( b'0' ..=b'9' ) = bit. peek ( ) {
116- params. context_count *= 10 ;
117- params. context_count += ( bit. next ( ) . unwrap ( ) - b'0' ) as usize ;
153+ context_count = Some ( context_count. unwrap ( ) * 10 ) ;
154+ context_count = Some (
155+ context_count. unwrap ( ) + ( bit. next ( ) . unwrap ( ) - b'0' ) as usize ,
156+ ) ;
118157 }
119158 }
120159 b'c' => {
@@ -129,30 +168,6 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
129168 }
130169 format = Some ( Format :: Ed ) ;
131170 }
132- b'u' => {
133- if format. is_some ( ) && format != Some ( Format :: Unified ) {
134- return Err ( "Conflicting output style options" . to_string ( ) ) ;
135- }
136- format = Some ( Format :: Unified ) ;
137- }
138- b'U' => {
139- if format. is_some ( ) && format != Some ( Format :: Unified ) {
140- return Err ( "Conflicting output style options" . to_string ( ) ) ;
141- }
142- format = Some ( Format :: Unified ) ;
143- let context_count_maybe = if bit. peek ( ) . is_some ( ) {
144- String :: from_utf8 ( bit. collect :: < Vec < u8 > > ( ) ) . ok ( )
145- } else {
146- opts. next ( ) . map ( |x| x. to_string_lossy ( ) . into_owned ( ) )
147- } ;
148- if let Some ( context_count_maybe) =
149- context_count_maybe. and_then ( |x| x. parse ( ) . ok ( ) )
150- {
151- params. context_count = context_count_maybe;
152- break ;
153- }
154- return Err ( "Invalid context count" . to_string ( ) ) ;
155- }
156171 _ => return Err ( format ! ( "Unknown option: {}" , String :: from_utf8_lossy( & [ b] ) ) ) ,
157172 }
158173 }
@@ -179,6 +194,9 @@ pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params,
179194 return Err ( format ! ( "Usage: {} <from> <to>" , exe. to_string_lossy( ) ) ) ;
180195 } ;
181196 params. format = format. unwrap_or ( Format :: default ( ) ) ;
197+ if context_count. is_some ( ) {
198+ params. context_count = context_count. unwrap ( ) ;
199+ }
182200 Ok ( params)
183201}
184202
@@ -212,6 +230,63 @@ mod tests {
212230 ) ;
213231 }
214232 #[ test]
233+ fn unified_valid ( ) {
234+ for args in [ vec ! [ "-u" ] , vec ! [ "--unified" ] , vec ! [ "--unified=" ] ] {
235+ let mut params = vec ! [ "diff" ] ;
236+ params. extend ( args) ;
237+ params. extend ( [ "foo" , "bar" ] ) ;
238+ assert_eq ! (
239+ Ok ( Params {
240+ from: os( "foo" ) ,
241+ to: os( "bar" ) ,
242+ format: Format :: Unified ,
243+ ..Default :: default ( )
244+ } ) ,
245+ parse_params( params. iter( ) . map( |x| os( x) ) )
246+ ) ;
247+ }
248+ for args in [
249+ vec ! [ "-u42" ] ,
250+ vec ! [ "-U42" ] ,
251+ vec ! [ "-U" , "42" ] ,
252+ vec ! [ "--unified=42" ] ,
253+ vec ! [ "-42u" ] ,
254+ ] {
255+ let mut params = vec ! [ "diff" ] ;
256+ params. extend ( args) ;
257+ params. extend ( [ "foo" , "bar" ] ) ;
258+ assert_eq ! (
259+ Ok ( Params {
260+ from: os( "foo" ) ,
261+ to: os( "bar" ) ,
262+ format: Format :: Unified ,
263+ context_count: 42 ,
264+ ..Default :: default ( )
265+ } ) ,
266+ parse_params( params. iter( ) . map( |x| os( x) ) )
267+ ) ;
268+ }
269+ }
270+ #[ test]
271+ fn unified_invalid ( ) {
272+ for args in [
273+ vec ! [ "-u" , "42" ] ,
274+ vec ! [ "-u=42" ] ,
275+ vec ! [ "-u=" ] ,
276+ vec ! [ "-U" ] ,
277+ vec ! [ "-U=42" ] ,
278+ vec ! [ "-U=" ] ,
279+ vec ! [ "--unified42" ] ,
280+ vec ! [ "--unified" , "42" ] ,
281+ vec ! [ "-42U" ] ,
282+ ] {
283+ let mut params = vec ! [ "diff" ] ;
284+ params. extend ( args) ;
285+ params. extend ( [ "foo" , "bar" ] ) ;
286+ assert ! ( parse_params( params. iter( ) . map( |x| os( x) ) ) . is_err( ) ) ;
287+ }
288+ }
289+ #[ test]
215290 fn context_count ( ) {
216291 assert_eq ! (
217292 Ok ( Params {
0 commit comments