Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 69 additions & 14 deletions src/uu/wc/src/wc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ struct Settings {
show_max_line_length: bool,
files0_from_stdin_mode: bool,
title_quoting_style: QuotingStyle,
total_when: TotalWhen,
}

impl Settings {
Expand All @@ -65,6 +66,7 @@ impl Settings {
show_max_line_length: matches.get_flag(options::MAX_LINE_LENGTH),
files0_from_stdin_mode,
title_quoting_style,
total_when: matches.get_one::<String>(options::TOTAL).unwrap().into(),
};

if settings.show_bytes
Expand All @@ -84,6 +86,7 @@ impl Settings {
show_max_line_length: false,
files0_from_stdin_mode,
title_quoting_style: settings.title_quoting_style,
total_when: settings.total_when,
}
}

Expand All @@ -107,6 +110,7 @@ pub mod options {
pub static FILES0_FROM: &str = "files0-from";
pub static LINES: &str = "lines";
pub static MAX_LINE_LENGTH: &str = "max-line-length";
pub static TOTAL: &str = "total";
pub static WORDS: &str = "words";
}

Expand Down Expand Up @@ -160,6 +164,37 @@ impl Input {
}
}

/// When to show the "total" line
#[derive(PartialEq)]
enum TotalWhen {
Auto,
Always,
Only,
Never,
}

impl From<&String> for TotalWhen {
fn from(s: &String) -> Self {
match s.as_ref() {
"auto" => Self::Auto,
"always" => Self::Always,
"only" => Self::Only,
"never" => Self::Never,
_ => unreachable!("Should have been caught by clap"),
}
}
}

impl TotalWhen {
fn is_total_row_visible(&self, num_inputs: usize) -> bool {
match self {
Self::Auto => num_inputs > 1,
Self::Always | Self::Only => true,
Self::Never => false,
}
}
}

#[derive(Debug)]
enum WcError {
FilesDisabled(String),
Expand Down Expand Up @@ -246,6 +281,15 @@ pub fn uu_app() -> Command {
.help("print the length of the longest line")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::TOTAL)
.long(options::TOTAL)
.value_parser(["auto", "always", "only", "never"])
.default_value("auto")
.hide_default_value(true)
.value_name("WHEN")
.help("when to print a line with total counts"),
)
.arg(
Arg::new(options::WORDS)
.short('w')
Expand Down Expand Up @@ -561,11 +605,19 @@ fn compute_number_width(inputs: &[Input], settings: &Settings) -> usize {
}

fn wc(inputs: &[Input], settings: &Settings) -> UResult<()> {
let number_width = compute_number_width(inputs, settings);

let mut total_word_count = WordCount::default();

let num_inputs = inputs.len();
let (number_width, are_stats_visible, total_row_title) =
if settings.total_when == TotalWhen::Only {
(1, false, None)
} else {
let number_width = compute_number_width(inputs, settings);
let title = Some(String::from("total"));

(number_width, true, title)
};

let is_total_row_visible = settings.total_when.is_total_row_visible(inputs.len());

for input in inputs {
let word_count = match word_count_from_input(input, settings) {
Expand Down Expand Up @@ -595,20 +647,23 @@ fn wc(inputs: &[Input], settings: &Settings) -> UResult<()> {
};
total_word_count += word_count;
let result = word_count.with_title(input.to_title(&settings.title_quoting_style));
if let Err(err) = print_stats(settings, &result, number_width) {
show!(USimpleError::new(
1,
format!(
"failed to print result for {}: {}",
&result.title.unwrap_or_else(|| String::from("<stdin>")),
err,
),
));

if are_stats_visible {
if let Err(err) = print_stats(settings, &result, number_width) {
show!(USimpleError::new(
1,
format!(
"failed to print result for {}: {}",
&result.title.unwrap_or_else(|| String::from("<stdin>")),
err,
),
));
}
}
}

if num_inputs > 1 {
let total_result = total_word_count.with_title(Some(String::from("total")));
if is_total_row_visible {
let total_result = total_word_count.with_title(total_row_title);
if let Err(err) = print_stats(settings, &total_result, number_width) {
show!(USimpleError::new(
1,
Expand Down
56 changes: 56 additions & 0 deletions tests/by-util/test_wc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,3 +464,59 @@ fn test_files0_from_with_stdin_try_read_from_stdin() {
.stderr_contains(MSG)
.stdout_is("");
}

#[test]
fn test_total_auto() {
new_ucmd!()
.args(&["lorem_ipsum.txt", "--total=auto"])
.run()
.stdout_is(" 13 109 772 lorem_ipsum.txt\n");

new_ucmd!()
.args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=auto"])
.run()
.stdout_is(
" 13 109 772 lorem_ipsum.txt\n 18 204 1115 moby_dick.txt\n 31 313 1887 total\n",
);
}

#[test]
fn test_total_always() {
new_ucmd!()
.args(&["lorem_ipsum.txt", "--total=always"])
.run()
.stdout_is(" 13 109 772 lorem_ipsum.txt\n 13 109 772 total\n");

new_ucmd!()
.args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=always"])
.run()
.stdout_is(
" 13 109 772 lorem_ipsum.txt\n 18 204 1115 moby_dick.txt\n 31 313 1887 total\n",
);
}

#[test]
fn test_total_never() {
new_ucmd!()
.args(&["lorem_ipsum.txt", "--total=never"])
.run()
.stdout_is(" 13 109 772 lorem_ipsum.txt\n");

new_ucmd!()
.args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=never"])
.run()
.stdout_is(" 13 109 772 lorem_ipsum.txt\n 18 204 1115 moby_dick.txt\n");
}

#[test]
fn test_total_only() {
new_ucmd!()
.args(&["lorem_ipsum.txt", "--total=only"])
.run()
.stdout_is("13 109 772\n");

new_ucmd!()
.args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=only"])
.run()
.stdout_is("31 313 1887\n");
}