1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-30 20:47:46 +00:00

Merge pull request #2162 from bashi8128/basename-clap

basename: move from getopts to clap
This commit is contained in:
Sylvestre Ledru 2021-05-04 10:59:19 +02:00 committed by GitHub
commit 1edf4064f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 39 deletions

View file

@ -15,6 +15,7 @@ edition = "2018"
path = "src/basename.rs" path = "src/basename.rs"
[dependencies] [dependencies]
clap = "2.33.2"
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" } uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }

View file

@ -10,83 +10,106 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use clap::{App, Arg};
use std::path::{is_separator, PathBuf}; use std::path::{is_separator, PathBuf};
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
static NAME: &str = "basename"; static VERSION: &str = env!("CARGO_PKG_VERSION");
static SYNTAX: &str = "NAME [SUFFIX]";
static SUMMARY: &str = "Print NAME with any leading directory components removed static SUMMARY: &str = "Print NAME with any leading directory components removed
If specified, also remove a trailing SUFFIX"; If specified, also remove a trailing SUFFIX";
static LONG_HELP: &str = "";
fn get_usage() -> String {
format!(
"{0} NAME [SUFFIX]
{0} OPTION... NAME...",
executable!()
)
}
pub mod options {
pub static MULTIPLE: &str = "multiple";
pub static NAME: &str = "name";
pub static SUFFIX: &str = "suffix";
pub static ZERO: &str = "zero";
}
pub fn uumain(args: impl uucore::Args) -> i32 { pub fn uumain(args: impl uucore::Args) -> i32 {
let args = args let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy) .collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any(); .accept_any();
let usage = get_usage();
// //
// Argument parsing // Argument parsing
// //
let matches = app!(SYNTAX, SUMMARY, LONG_HELP) let matches = App::new(executable!())
.optflag( .version(VERSION)
"a", .about(SUMMARY)
"multiple", .usage(&usage[..])
"Support more than one argument. Treat every argument as a name.", .arg(
Arg::with_name(options::MULTIPLE)
.short("a")
.long(options::MULTIPLE)
.help("support multiple arguments and treat each as a NAME"),
) )
.optopt( .arg(Arg::with_name(options::NAME).multiple(true).hidden(true))
"s", .arg(
"suffix", Arg::with_name(options::SUFFIX)
"Remove a trailing suffix. This option implies the -a option.", .short("s")
"SUFFIX", .long(options::SUFFIX)
.value_name("SUFFIX")
.help("remove a trailing SUFFIX; implies -a"),
) )
.optflag( .arg(
"z", Arg::with_name(options::ZERO)
"zero", .short("z")
"Output a zero byte (ASCII NUL) at the end of each line, rather than a newline.", .long(options::ZERO)
.help("end each output line with NUL, not newline"),
) )
.parse(args); .get_matches_from(args);
// too few arguments // too few arguments
if matches.free.is_empty() { if !matches.is_present(options::NAME) {
crash!( crash!(
1, 1,
"{0}: {1}\nTry '{0} --help' for more information.", "{1}\nTry '{0} --help' for more information.",
NAME, executable!(),
"missing operand" "missing operand"
); );
} }
let opt_s = matches.opt_present("s");
let opt_a = matches.opt_present("a"); let opt_suffix = matches.is_present(options::SUFFIX);
let opt_z = matches.opt_present("z"); let opt_multiple = matches.is_present(options::MULTIPLE);
let multiple_paths = opt_s || opt_a; let opt_zero = matches.is_present(options::ZERO);
let multiple_paths = opt_suffix || opt_multiple;
// too many arguments // too many arguments
if !multiple_paths && matches.free.len() > 2 { if !multiple_paths && matches.occurrences_of(options::NAME) > 2 {
crash!( crash!(
1, 1,
"{0}: extra operand '{1}'\nTry '{0} --help' for more information.", "extra operand '{1}'\nTry '{0} --help' for more information.",
NAME, executable!(),
matches.free[2] matches.values_of(options::NAME).unwrap().nth(2).unwrap()
); );
} }
let suffix = if opt_s { let suffix = if opt_suffix {
matches.opt_str("s").unwrap() matches.value_of(options::SUFFIX).unwrap()
} else if !opt_a && matches.free.len() > 1 { } else if !opt_multiple && matches.occurrences_of(options::NAME) > 1 {
matches.free[1].clone() matches.values_of(options::NAME).unwrap().nth(1).unwrap()
} else { } else {
"".to_owned() ""
}; };
// //
// Main Program Processing // Main Program Processing
// //
let paths = if multiple_paths { let paths: Vec<_> = if multiple_paths {
&matches.free[..] matches.values_of(options::NAME).unwrap().collect()
} else { } else {
&matches.free[0..1] matches.values_of(options::NAME).unwrap().take(1).collect()
}; };
let line_ending = if opt_z { "\0" } else { "\n" }; let line_ending = if opt_zero { "\0" } else { "\n" };
for path in paths { for path in paths {
print!("{}{}", basename(&path, &suffix), line_ending); print!("{}{}", basename(&path, &suffix), line_ending);
} }

View file

@ -1,6 +1,29 @@
use crate::common::util::*; use crate::common::util::*;
use std::ffi::OsStr; use std::ffi::OsStr;
#[test]
fn test_help() {
for help_flg in vec!["-h", "--help"] {
new_ucmd!()
.arg(&help_flg)
.succeeds()
.no_stderr()
.stdout_contains("USAGE:");
}
}
#[test]
fn test_version() {
for version_flg in vec!["-V", "--version"] {
assert!(new_ucmd!()
.arg(&version_flg)
.succeeds()
.no_stderr()
.stdout_str()
.starts_with("basename"));
}
}
#[test] #[test]
fn test_directory() { fn test_directory() {
new_ucmd!() new_ucmd!()
@ -81,11 +104,25 @@ fn test_no_args() {
expect_error(vec![]); expect_error(vec![]);
} }
#[test]
fn test_no_args_output() {
new_ucmd!()
.fails()
.stderr_is("basename: error: missing operand\nTry 'basename --help' for more information.");
}
#[test] #[test]
fn test_too_many_args() { fn test_too_many_args() {
expect_error(vec!["a", "b", "c"]); expect_error(vec!["a", "b", "c"]);
} }
#[test]
fn test_too_many_args_output() {
new_ucmd!().args(&["a", "b", "c"]).fails().stderr_is(
"basename: error: extra operand 'c'\nTry 'basename --help' for more information.",
);
}
fn test_invalid_utf8_args(os_str: &OsStr) { fn test_invalid_utf8_args(os_str: &OsStr) {
let test_vec = vec![os_str.to_os_string()]; let test_vec = vec![os_str.to_os_string()];
new_ucmd!().args(&test_vec).succeeds().stdout_is("fo<EFBFBD>o\n"); new_ucmd!().args(&test_vec).succeeds().stdout_is("fo<EFBFBD>o\n");