@@ -268,7 +268,7 @@ impl StrUtils for str {
268268 }
269269}
270270
271- #[ derive( PartialEq ) ]
271+ #[ derive( PartialEq , Debug ) ]
272272enum ParseState {
273273 Global ,
274274 Matched ,
@@ -279,12 +279,8 @@ enum ParseState {
279279use std:: collections:: HashMap ;
280280use uucore:: { format_usage, parse_glob} ;
281281
282- #[ allow( clippy:: cognitive_complexity) ]
283- fn parse < T > ( lines : T , fmt : & OutputFmt , fp : & str ) -> Result < String , String >
284- where
285- T : IntoIterator ,
286- T :: Item : Borrow < str > ,
287- {
282+ // Initialize result and table
283+ fn init_parse ( fmt : & OutputFmt ) -> ( String , HashMap < & ' static str , & ' static str > ) {
288284 // 1790 > $(dircolors | wc -m)
289285 let mut result = String :: with_capacity ( 1790 ) ;
290286 match fmt {
@@ -332,10 +328,84 @@ where
332328 table. insert ( "capability" , "ca" ) ;
333329 table. insert ( "multihardlink" , "mh" ) ;
334330 table. insert ( "clrtoeol" , "cl" ) ;
331+ ( result, table)
332+ }
333+
334+ fn handle_terminal_type ( lower : & str , val : & str , term : & str , state : & mut ParseState ) {
335+ if lower == "term" || lower == "colorterm" {
336+ if term. fnmatch ( val) {
337+ * state = ParseState :: Matched ;
338+ } else if * state != ParseState :: Matched {
339+ * state = ParseState :: Pass ;
340+ }
341+ } else if * state == ParseState :: Matched {
342+ // prevent subsequent mismatched TERM from cancelling the input
343+ * state = ParseState :: Continue ;
344+ }
345+ }
346+
347+ fn process_line (
348+ line : & str ,
349+ fmt : & OutputFmt ,
350+ state : & mut ParseState ,
351+ table : & HashMap < & str , & str > ,
352+ result : & mut String ,
353+ num : usize ,
354+ fp : & str ,
355+ ) -> Result < ( ) , String > {
356+ let ( key, val) = line. split_two ( ) ;
357+ if val. is_empty ( ) {
358+ return Err ( format ! (
359+ "{}:{}: invalid line; missing second token" ,
360+ fp. maybe_quote( ) ,
361+ num
362+ ) ) ;
363+ }
364+ let lower = key. to_lowercase ( ) ;
365+
366+ if * state != ParseState :: Pass {
367+ if key. starts_with ( '.' ) {
368+ // handle file extension
369+ add_formatting ( result, fmt, key, val, "file extension" ) ;
370+ } else if key. starts_with ( '*' ) {
371+ // handle wildcard
372+ add_formatting ( result, fmt, key, val, "wildcard" ) ;
373+ } else if let Some ( s) = table. get ( lower. as_str ( ) ) {
374+ // handle known keyword
375+ add_formatting ( result, fmt, s, val, "keyword" ) ;
376+ } else if lower != "options" && lower != "color" && lower != "eightbit" {
377+ // unrecognized keyword
378+ return Err ( format ! (
379+ "{}:{}: unrecognized keyword {}" ,
380+ fp. maybe_quote( ) ,
381+ num,
382+ key
383+ ) ) ;
384+ }
385+ }
386+
387+ Ok ( ( ) )
388+ }
389+
390+ fn add_formatting ( result : & mut String , fmt : & OutputFmt , key : & str , val : & str , description : & str ) {
391+ match fmt {
392+ OutputFmt :: Display => {
393+ result. push_str ( format ! ( "\x1b [{val}m{key} ({description})\t {val}\x1b [0m\n " ) . as_str ( ) ) ;
394+ }
395+ _ => {
396+ result. push_str ( format ! ( "{key}={val}:" ) . as_str ( ) ) ;
397+ }
398+ }
399+ }
335400
401+ fn parse < T > ( lines : T , fmt : & OutputFmt , fp : & str ) -> Result < String , String >
402+ where
403+ T : IntoIterator ,
404+ T :: Item : Borrow < str > ,
405+ {
406+ let ( mut result, table) = init_parse ( fmt) ;
336407 let term = env:: var ( "TERM" ) . unwrap_or_else ( |_| "none" . to_owned ( ) ) ;
337408 let term = term. as_str ( ) ;
338-
339409 let mut state = ParseState :: Global ;
340410
341411 for ( num, line) in lines. into_iter ( ) . enumerate ( ) {
@@ -346,60 +416,11 @@ where
346416 }
347417
348418 let line = escape ( line) ;
349-
350419 let ( key, val) = line. split_two ( ) ;
351- if val. is_empty ( ) {
352- return Err ( format ! (
353- "{}:{}: invalid line; missing second token" ,
354- fp. maybe_quote( ) ,
355- num
356- ) ) ;
357- }
358420 let lower = key. to_lowercase ( ) ;
359421
360- if lower == "term" || lower == "colorterm" {
361- if term. fnmatch ( val) {
362- state = ParseState :: Matched ;
363- } else if state != ParseState :: Matched {
364- state = ParseState :: Pass ;
365- }
366- } else {
367- if state == ParseState :: Matched {
368- // prevent subsequent mismatched TERM from
369- // cancelling the input
370- state = ParseState :: Continue ;
371- }
372- if state != ParseState :: Pass {
373- if key. starts_with ( '.' ) {
374- if * fmt == OutputFmt :: Display {
375- result. push_str ( format ! ( "\x1b [{val}m*{key}\t {val}\x1b [0m\n " ) . as_str ( ) ) ;
376- } else {
377- result. push_str ( format ! ( "*{key}={val}:" ) . as_str ( ) ) ;
378- }
379- } else if key. starts_with ( '*' ) {
380- if * fmt == OutputFmt :: Display {
381- result. push_str ( format ! ( "\x1b [{val}m{key}\t {val}\x1b [0m\n " ) . as_str ( ) ) ;
382- } else {
383- result. push_str ( format ! ( "{key}={val}:" ) . as_str ( ) ) ;
384- }
385- } else if lower == "options" || lower == "color" || lower == "eightbit" {
386- // Slackware only. Ignore
387- } else if let Some ( s) = table. get ( lower. as_str ( ) ) {
388- if * fmt == OutputFmt :: Display {
389- result. push_str ( format ! ( "\x1b [{val}m{s}\t {val}\x1b [0m\n " ) . as_str ( ) ) ;
390- } else {
391- result. push_str ( format ! ( "{s}={val}:" ) . as_str ( ) ) ;
392- }
393- } else {
394- return Err ( format ! (
395- "{}:{}: unrecognized keyword {}" ,
396- fp. maybe_quote( ) ,
397- num,
398- key
399- ) ) ;
400- }
401- }
402- }
422+ handle_terminal_type ( & lower, val, term, & mut state) ;
423+ process_line ( & line, fmt, & mut state, & table, & mut result, num, fp) ?;
403424 }
404425
405426 match fmt {
@@ -439,12 +460,87 @@ fn escape(s: &str) -> String {
439460#[ cfg( test) ]
440461mod tests {
441462 use super :: escape;
442-
463+ use crate :: { handle_terminal_type, process_line} ;
464+ use crate :: { OutputFmt , ParseState } ;
465+ use std:: collections:: HashMap ;
443466 #[ test]
444467 fn test_escape ( ) {
445468 assert_eq ! ( "" , escape( "" ) ) ;
446469 assert_eq ! ( "'\\ ''" , escape( "'" ) ) ;
447470 assert_eq ! ( "\\ :" , escape( ":" ) ) ;
448471 assert_eq ! ( "\\ :" , escape( "\\ :" ) ) ;
449472 }
473+
474+ // Test for handle_terminal_type function
475+ #[ test]
476+ fn test_handle_terminal_type ( ) {
477+ let mut state = ParseState :: Global ;
478+ let term = "xterm-256color" ;
479+
480+ // Test with matching TERM
481+ handle_terminal_type ( "term" , "xterm*" , term, & mut state) ;
482+ assert_eq ! ( state, ParseState :: Matched ) ;
483+
484+ // Reset state and test with non-matching TERM
485+ state = ParseState :: Global ;
486+ handle_terminal_type ( "term" , "vt100" , term, & mut state) ;
487+ assert_eq ! ( state, ParseState :: Pass ) ;
488+ }
489+
490+ #[ test]
491+ fn test_process_line ( ) {
492+ let mut result = String :: new ( ) ;
493+ let mut state = ParseState :: Global ;
494+ let fmt = OutputFmt :: Shell ;
495+ let num = 1 ;
496+ let fp = "test_file" ;
497+ let mut table = HashMap :: new ( ) ;
498+ table. insert ( "dir" , "di" ) ;
499+
500+ // Test processing a valid line
501+ let line = "DIR 01;34" ;
502+ let res = process_line ( line, & fmt, & mut state, & table, & mut result, num, fp) ;
503+ assert ! ( res. is_ok( ) ) ;
504+ assert_eq ! ( result, "di=01;34:" ) ;
505+
506+ // Test processing an invalid line (no value part)
507+ result. clear ( ) ;
508+ let invalid_line = "DIR" ;
509+ let res = process_line ( invalid_line, & fmt, & mut state, & table, & mut result, num, fp) ;
510+ assert ! ( res. is_err( ) ) ;
511+ }
512+
513+ #[ test]
514+ fn test_handle_terminal_type_various_cases ( ) {
515+ let term = "xterm-256color" ;
516+
517+ // Test with matching TERM
518+ let mut state = ParseState :: Global ;
519+ handle_terminal_type ( "term" , "xterm*" , term, & mut state) ;
520+ assert_eq ! ( state, ParseState :: Matched ) ;
521+
522+ // Test with non-matching TERM
523+ state = ParseState :: Global ;
524+ handle_terminal_type ( "term" , "vt100" , term, & mut state) ;
525+ assert_eq ! ( state, ParseState :: Pass ) ;
526+
527+ // Test with matching COLORTERM
528+ state = ParseState :: Global ;
529+ handle_terminal_type ( "colorterm" , "xterm*" , term, & mut state) ;
530+ assert_eq ! ( state, ParseState :: Matched ) ;
531+
532+ // Test with non-matching COLORTERM
533+ state = ParseState :: Global ;
534+ handle_terminal_type ( "colorterm" , "vt100" , term, & mut state) ;
535+ assert_eq ! ( state, ParseState :: Pass ) ;
536+
537+ // Test with already matched state
538+ state = ParseState :: Matched ;
539+ handle_terminal_type ( "term" , "vt100" , term, & mut state) ;
540+ assert_eq ! ( state, ParseState :: Matched ) ;
541+
542+ state = ParseState :: Pass ;
543+ handle_terminal_type ( "term" , "xterm*" , term, & mut state) ;
544+ assert_eq ! ( state, ParseState :: Matched ) ;
545+ }
450546}
0 commit comments