1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

shuf: treat -e as a flag, not as a multi-value arg

This commit is contained in:
Ben Wiederhake 2024-02-20 02:20:36 +01:00
parent f7821cd0d2
commit a59924ece5
2 changed files with 97 additions and 17 deletions

View file

@ -12,7 +12,7 @@ use rand::RngCore;
use std::fs::File;
use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write};
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::{format_usage, help_about, help_usage};
mod rand_read_adapter;
@ -42,15 +42,21 @@ mod options {
pub static RANDOM_SOURCE: &str = "random-source";
pub static REPEAT: &str = "repeat";
pub static ZERO_TERMINATED: &str = "zero-terminated";
pub static FILE: &str = "file";
pub static FILE_OR_ARGS: &str = "file-or-args";
}
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().try_get_matches_from(args)?;
let mode = if let Some(args) = matches.get_many::<String>(options::ECHO) {
Mode::Echo(args.map(String::from).collect())
let mode = if matches.get_flag(options::ECHO) {
Mode::Echo(
matches
.get_many::<String>(options::FILE_OR_ARGS)
.unwrap_or_default()
.map(String::from)
.collect(),
)
} else if let Some(range) = matches.get_one::<String>(options::INPUT_RANGE) {
match parse_range(range) {
Ok(m) => Mode::InputRange(m),
@ -59,13 +65,17 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
}
}
} else {
Mode::Default(
matches
.get_one::<String>(options::FILE)
.map(|s| s.as_str())
.unwrap_or("-")
.to_string(),
)
let mut operands = matches
.get_many::<String>(options::FILE_OR_ARGS)
.unwrap_or_default();
let file = operands.next().cloned().unwrap_or("-".into());
if let Some(second_file) = operands.next() {
return Err(UUsageError::new(
1,
format!("unexpected argument '{second_file}' found"),
));
};
Mode::Default(file)
};
let options = Options {
@ -124,11 +134,9 @@ pub fn uu_app() -> Command {
Arg::new(options::ECHO)
.short('e')
.long(options::ECHO)
.value_name("ARG")
.help("treat each ARG as an input line")
.use_value_delimiter(false)
.num_args(0..)
.action(clap::ArgAction::Append)
.action(clap::ArgAction::SetTrue)
.overrides_with(options::ECHO)
.conflicts_with(options::INPUT_RANGE),
)
.arg(
@ -137,7 +145,7 @@ pub fn uu_app() -> Command {
.long(options::INPUT_RANGE)
.value_name("LO-HI")
.help("treat each number LO through HI as an input line")
.conflicts_with(options::FILE),
.conflicts_with(options::FILE_OR_ARGS),
)
.arg(
Arg::new(options::HEAD_COUNT)
@ -178,7 +186,11 @@ pub fn uu_app() -> Command {
.action(ArgAction::SetTrue)
.overrides_with(options::ZERO_TERMINATED),
)
.arg(Arg::new(options::FILE).value_hint(clap::ValueHint::FilePath))
.arg(
Arg::new(options::FILE_OR_ARGS)
.action(clap::ArgAction::Append)
.value_hint(clap::ValueHint::FilePath),
)
}
fn read_input_file(filename: &str) -> UResult<Vec<u8>> {

View file

@ -32,6 +32,28 @@ fn test_output_is_random_permutation() {
assert_eq!(result_seq, input_seq, "Output is not a permutation");
}
#[test]
fn test_explicit_stdin_file() {
let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let input = input_seq
.iter()
.map(ToString::to_string)
.collect::<Vec<String>>()
.join("\n");
let result = new_ucmd!().arg("-").pipe_in(input.as_bytes()).succeeds();
result.no_stderr();
let mut result_seq: Vec<i32> = result
.stdout_str()
.split('\n')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
result_seq.sort_unstable();
assert_eq!(result_seq, input_seq, "Output is not a permutation");
}
#[test]
fn test_zero_termination() {
let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
@ -116,6 +138,36 @@ fn test_echo_multi() {
assert_eq!(result_seq, ["a", "b", "c"], "Output is not a permutation");
}
#[test]
fn test_echo_postfix() {
let result = new_ucmd!().arg("a").arg("b").arg("c").arg("-e").succeeds();
result.no_stderr();
let mut result_seq: Vec<String> = result
.stdout_str()
.split('\n')
.filter(|x| !x.is_empty())
.map(|x| x.into())
.collect();
result_seq.sort_unstable();
assert_eq!(result_seq, ["a", "b", "c"], "Output is not a permutation");
}
#[test]
fn test_echo_short_collapsed_zero() {
let result = new_ucmd!().arg("-ez").arg("a").arg("b").arg("c").succeeds();
result.no_stderr();
let mut result_seq: Vec<String> = result
.stdout_str()
.split('\0')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
result_seq.sort_unstable();
assert_eq!(result_seq, ["a", "b", "c"], "Output is not a permutation");
}
#[test]
fn test_head_count() {
let repeat_limit = 5;
@ -365,6 +417,22 @@ fn test_shuf_multiple_outputs() {
.stderr_contains("cannot be used multiple times");
}
#[test]
fn test_shuf_two_input_files() {
new_ucmd!()
.args(&["file_a", "file_b"])
.fails()
.stderr_contains("unexpected argument 'file_b' found");
}
#[test]
fn test_shuf_three_input_files() {
new_ucmd!()
.args(&["file_a", "file_b", "file_c"])
.fails()
.stderr_contains("unexpected argument 'file_b' found");
}
#[test]
fn test_shuf_invalid_input_line_count() {
new_ucmd!()