Skip to content

Commit ea8c8a4

Browse files
committed
coreutils: split function parse + add tests
1 parent 319315c commit ea8c8a4

File tree

1 file changed

+156
-60
lines changed

1 file changed

+156
-60
lines changed

src/uu/dircolors/src/dircolors.rs

Lines changed: 156 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ impl StrUtils for str {
268268
}
269269
}
270270

271-
#[derive(PartialEq)]
271+
#[derive(PartialEq, Debug)]
272272
enum ParseState {
273273
Global,
274274
Matched,
@@ -279,12 +279,8 @@ enum ParseState {
279279
use std::collections::HashMap;
280280
use 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)]
440461
mod 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

Comments
 (0)