Skip to content

Commit

Permalink
Merge pull request #331 from cakebaker/upgrade_to_clap_v4_4
Browse files Browse the repository at this point in the history
xargs: upgrade to clap v4.4
  • Loading branch information
sylvestre authored Mar 14, 2024
2 parents 6dec804 + e66a7c1 commit ce2ca6b
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 137 deletions.
71 changes: 3 additions & 68 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ authors = ["uutils developers"]

[dependencies]
chrono = "0.4"
clap = "2.34"
clap = "4.4"
faccess = "0.2.4"
walkdir = "2.5"
regex = "1.7"
Expand Down
130 changes: 63 additions & 67 deletions src/xargs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::{
process::{Command, Stdio},
};

use clap::{crate_version, App, AppSettings, Arg};
use clap::{crate_version, Arg, ArgAction};

mod options {
pub const COMMAND: &str = "COMMAND";
Expand Down Expand Up @@ -719,128 +719,124 @@ fn parse_delimiter(s: &str) -> Result<u8, String> {
}
}

fn validate_positive_usize(s: String) -> Result<(), String> {
fn validate_positive_usize(s: &str) -> Result<usize, String> {
match s.parse::<usize>() {
Ok(v) if v > 0 => Ok(()),
Ok(v) if v > 0 => Ok(v),
Ok(v) => Err(format!("Value must be > 0, not: {v}")),
Err(e) => Err(e.to_string()),
}
}

fn do_xargs(args: &[&str]) -> Result<CommandResult, XargsError> {
let matches = App::new("xargs")
let matches = clap::Command::new("xargs")
.version(crate_version!())
.about("Run commands using arguments derived from standard input")
.settings(&[AppSettings::TrailingVarArg])
.arg(
Arg::with_name(options::COMMAND)
.takes_value(true)
.multiple(true)
.help("The command to run"),
Arg::new(options::COMMAND)
.help("The command to run")
.trailing_var_arg(true)
.num_args(0..)
.value_parser(clap::value_parser!(OsString)),
)
.arg(
Arg::with_name(options::ARG_FILE)
.short("a")
Arg::new(options::ARG_FILE)
.short('a')
.long(options::ARG_FILE)
.takes_value(true)
.help("Read arguments from the given file instead of stdin"),
)
.arg(
Arg::with_name(options::DELIMITER)
.short("d")
Arg::new(options::DELIMITER)
.short('d')
.long(options::DELIMITER)
.takes_value(true)
.validator(|s| parse_delimiter(&s).map(|_| ()))
.help("Use the given delimiter to split the input"),
.help("Use the given delimiter to split the input")
.value_parser(parse_delimiter),
)
.arg(
Arg::with_name(options::EXIT)
.short("x")
Arg::new(options::EXIT)
.short('x')
.long(options::EXIT)
.help(
"Exit if the number of arguments allowed by -L or -n do not \
fit into the number of allowed characters",
),
)
.action(ArgAction::SetTrue),
)
.arg(
Arg::with_name(options::MAX_ARGS)
.short("n")
.takes_value(true)
Arg::new(options::MAX_ARGS)
.short('n')
.long(options::MAX_ARGS)
.validator(validate_positive_usize)
.help(
"Set the max number of arguments read from stdin to be passed \
to each command invocation (mutually exclusive with -L)",
),
)
.value_parser(validate_positive_usize),
)
.arg(
Arg::with_name(options::MAX_LINES)
.short("L")
.takes_value(true)
.validator(validate_positive_usize)
Arg::new(options::MAX_LINES)
.short('L')
.help(
"Set the max number of lines from stdin to be passed to each \
command invocation (mutually exclusive with -n)",
),
)
.value_parser(validate_positive_usize),
)
.arg(
Arg::with_name(options::MAX_PROCS)
.short("P")
.takes_value(true)
Arg::new(options::MAX_PROCS)
.short('P')
.long(options::MAX_PROCS)
.help("Run up to this many commands in parallel [NOT IMPLEMENTED]"),
.help("Run up to this many commands in parallel [NOT IMPLEMENTED]")
.value_parser(clap::value_parser!(usize)),
)
.arg(
Arg::with_name(options::NO_RUN_IF_EMPTY)
.short("r")
Arg::new(options::NO_RUN_IF_EMPTY)
.short('r')
.long(options::NO_RUN_IF_EMPTY)
.help("If there are no input arguments, do not run the command at all"),
.help("If there are no input arguments, do not run the command at all")
.action(ArgAction::SetTrue),
)
.arg(
Arg::with_name(options::NULL)
.short("0")
Arg::new(options::NULL)
.short('0')
.long(options::NULL)
.help("Split the input by null terminators rather than whitespace"),
.help("Split the input by null terminators rather than whitespace")
.action(ArgAction::SetTrue),
)
.arg(
Arg::with_name(options::MAX_CHARS)
.short("s")
Arg::new(options::MAX_CHARS)
.short('s')
.long(options::MAX_CHARS)
.takes_value(true)
.validator(validate_positive_usize)
.help(
"Set the max number of characters to be passed to each \
invocation",
),
)
.value_parser(validate_positive_usize),
)
.arg(
Arg::with_name(options::VERBOSE)
.short("t")
Arg::new(options::VERBOSE)
.short('t')
.long(options::VERBOSE)
.help("Be verbose"),
.help("Be verbose")
.action(ArgAction::SetTrue),
)
.get_matches_from(args);
.try_get_matches_from(args);

let matches = match matches {
Ok(m) => m,
Err(e) => return Err(XargsError::from(e.to_string())),
};

let options = Options {
arg_file: matches
.value_of(options::ARG_FILE)
.get_one::<String>(options::ARG_FILE)
.map(|value| value.to_owned()),
delimiter: matches
.value_of(options::DELIMITER)
.map(|value| parse_delimiter(value).unwrap()),
exit_if_pass_char_limit: matches.is_present(options::EXIT),
max_args: matches
.value_of(options::MAX_ARGS)
.map(|value| value.parse().unwrap()),
max_chars: matches
.value_of(options::MAX_CHARS)
.map(|value| value.parse().unwrap()),
max_lines: matches
.value_of(options::MAX_LINES)
.map(|value| value.parse().unwrap()),
no_run_if_empty: matches.is_present(options::NO_RUN_IF_EMPTY),
null: matches.is_present(options::NULL),
verbose: matches.is_present(options::VERBOSE),
delimiter: matches.get_one::<u8>(options::DELIMITER).copied(),
exit_if_pass_char_limit: matches.get_flag(options::EXIT),
max_args: matches.get_one::<usize>(options::MAX_ARGS).copied(),
max_chars: matches.get_one::<usize>(options::MAX_CHARS).copied(),
max_lines: matches.get_one::<usize>(options::MAX_LINES).copied(),
no_run_if_empty: matches.get_flag(options::NO_RUN_IF_EMPTY),
null: matches.get_flag(options::NULL),
verbose: matches.get_flag(options::VERBOSE),
};

let delimiter = match (options.delimiter, options.null) {
Expand All @@ -858,7 +854,7 @@ fn do_xargs(args: &[&str]) -> Result<CommandResult, XargsError> {
(None, false) => None,
};

let action = match matches.values_of_os(options::COMMAND) {
let action = match matches.get_many::<OsString>(options::COMMAND) {
Some(args) if args.len() > 0 => {
ExecAction::Command(args.map(|arg| arg.to_owned()).collect())
}
Expand Down
2 changes: 1 addition & 1 deletion tests/xargs_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ fn xargs_delim() {
.assert()
.failure()
.code(1)
.stderr(predicate::str::contains("Invalid"))
.stderr(predicate::str::contains("invalid"))
.stdout(predicate::str::is_empty());
}

Expand Down

0 comments on commit ce2ca6b

Please sign in to comment.