ls: accept shortcuts for stringly-enum arguments

This commit is contained in:
Ben Wiederhake 2024-04-01 08:06:18 +02:00
parent 27a81f3d32
commit 3877d14504
2 changed files with 128 additions and 32 deletions

View file

@ -6,7 +6,7 @@
// spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm stringly // spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm stringly
use clap::{ use clap::{
builder::{NonEmptyStringValueParser, ValueParser}, builder::{NonEmptyStringValueParser, PossibleValue, ValueParser},
crate_version, Arg, ArgAction, Command, crate_version, Arg, ArgAction, Command,
}; };
use glob::{MatchOptions, Pattern}; use glob::{MatchOptions, Pattern};
@ -62,6 +62,7 @@ use uucore::{
format_usage, format_usage,
fs::display_permissions, fs::display_permissions,
parse_size::parse_size_u64, parse_size::parse_size_u64,
shortcut_value_parser::ShortcutValueParser,
version_cmp::version_cmp, version_cmp::version_cmp,
}; };
use uucore::{help_about, help_section, help_usage, parse_glob, show, show_error, show_warning}; use uucore::{help_about, help_section, help_usage, parse_glob, show, show_error, show_warning};
@ -1203,7 +1204,7 @@ pub fn uu_app() -> Command {
Arg::new(options::FORMAT) Arg::new(options::FORMAT)
.long(options::FORMAT) .long(options::FORMAT)
.help("Set the display format.") .help("Set the display format.")
.value_parser([ .value_parser(ShortcutValueParser::new([
"long", "long",
"verbose", "verbose",
"single-column", "single-column",
@ -1212,7 +1213,7 @@ pub fn uu_app() -> Command {
"across", "across",
"horizontal", "horizontal",
"commas", "commas",
]) ]))
.hide_possible_values(true) .hide_possible_values(true)
.require_equals(true) .require_equals(true)
.overrides_with_all([ .overrides_with_all([
@ -1303,9 +1304,11 @@ pub fn uu_app() -> Command {
Arg::new(options::HYPERLINK) Arg::new(options::HYPERLINK)
.long(options::HYPERLINK) .long(options::HYPERLINK)
.help("hyperlink file names WHEN") .help("hyperlink file names WHEN")
.value_parser([ .value_parser(ShortcutValueParser::new([
"always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", PossibleValue::new("always").alias("yes").alias("force"),
]) PossibleValue::new("auto").alias("tty").alias("if-tty"),
PossibleValue::new("never").alias("no").alias("none"),
]))
.require_equals(true) .require_equals(true)
.num_args(0..=1) .num_args(0..=1)
.default_missing_value("always") .default_missing_value("always")
@ -1351,15 +1354,15 @@ pub fn uu_app() -> Command {
Arg::new(options::QUOTING_STYLE) Arg::new(options::QUOTING_STYLE)
.long(options::QUOTING_STYLE) .long(options::QUOTING_STYLE)
.help("Set quoting style.") .help("Set quoting style.")
.value_parser([ .value_parser(ShortcutValueParser::new([
"literal", PossibleValue::new("literal"),
"shell", PossibleValue::new("shell"),
"shell-always", PossibleValue::new("shell-escape"),
"shell-escape", PossibleValue::new("shell-always"),
"shell-escape-always", PossibleValue::new("shell-escape-always"),
"c", PossibleValue::new("c").alias("c-maybe"),
"escape", PossibleValue::new("escape"),
]) ]))
.overrides_with_all([ .overrides_with_all([
options::QUOTING_STYLE, options::QUOTING_STYLE,
options::quoting::LITERAL, options::quoting::LITERAL,
@ -1434,9 +1437,11 @@ pub fn uu_app() -> Command {
\tbirth time: birth, creation;", \tbirth time: birth, creation;",
) )
.value_name("field") .value_name("field")
.value_parser([ .value_parser(ShortcutValueParser::new([
"atime", "access", "use", "ctime", "status", "birth", "creation", PossibleValue::new("atime").alias("access").alias("use"),
]) PossibleValue::new("ctime").alias("status"),
PossibleValue::new("birth").alias("creation"),
]))
.hide_possible_values(true) .hide_possible_values(true)
.require_equals(true) .require_equals(true)
.overrides_with_all([options::TIME, options::time::ACCESS, options::time::CHANGE]), .overrides_with_all([options::TIME, options::time::ACCESS, options::time::CHANGE]),
@ -1496,7 +1501,7 @@ pub fn uu_app() -> Command {
.long(options::SORT) .long(options::SORT)
.help("Sort by <field>: name, none (-U), time (-t), size (-S), extension (-X) or width") .help("Sort by <field>: name, none (-U), time (-t), size (-S), extension (-X) or width")
.value_name("field") .value_name("field")
.value_parser(["name", "none", "time", "size", "version", "extension", "width"]) .value_parser(ShortcutValueParser::new(["name", "none", "time", "size", "version", "extension", "width"]))
.require_equals(true) .require_equals(true)
.overrides_with_all([ .overrides_with_all([
options::SORT, options::SORT,
@ -1744,9 +1749,11 @@ pub fn uu_app() -> Command {
Arg::new(options::COLOR) Arg::new(options::COLOR)
.long(options::COLOR) .long(options::COLOR)
.help("Color output based on file type.") .help("Color output based on file type.")
.value_parser([ .value_parser(ShortcutValueParser::new([
"always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", PossibleValue::new("always").alias("yes").alias("force"),
]) PossibleValue::new("auto").alias("tty").alias("if-tty"),
PossibleValue::new("never").alias("no").alias("none"),
]))
.require_equals(true) .require_equals(true)
.num_args(0..=1), .num_args(0..=1),
) )
@ -1757,7 +1764,7 @@ pub fn uu_app() -> Command {
"Append indicator with style WORD to entry names: \ "Append indicator with style WORD to entry names: \
none (default), slash (-p), file-type (--file-type), classify (-F)", none (default), slash (-p), file-type (--file-type), classify (-F)",
) )
.value_parser(["none", "slash", "file-type", "classify"]) .value_parser(ShortcutValueParser::new(["none", "slash", "file-type", "classify"]))
.overrides_with_all([ .overrides_with_all([
options::indicator_style::FILE_TYPE, options::indicator_style::FILE_TYPE,
options::indicator_style::SLASH, options::indicator_style::SLASH,
@ -1788,9 +1795,11 @@ pub fn uu_app() -> Command {
--dereference-command-line-symlink-to-dir options are specified.", --dereference-command-line-symlink-to-dir options are specified.",
) )
.value_name("when") .value_name("when")
.value_parser([ .value_parser(ShortcutValueParser::new([
"always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", PossibleValue::new("always").alias("yes").alias("force"),
]) PossibleValue::new("auto").alias("tty").alias("if-tty"),
PossibleValue::new("never").alias("no").alias("none"),
]))
.default_missing_value("always") .default_missing_value("always")
.require_equals(true) .require_equals(true)
.num_args(0..=1) .num_args(0..=1)

View file

@ -27,6 +27,7 @@ const LONG_ARGS: &[&str] = &[
"-l", "-l",
"--long", "--long",
"--format=long", "--format=long",
"--format=lon",
"--for=long", "--for=long",
"--format=verbose", "--format=verbose",
"--for=verbose", "--for=verbose",
@ -35,6 +36,7 @@ const LONG_ARGS: &[&str] = &[
const ACROSS_ARGS: &[&str] = &[ const ACROSS_ARGS: &[&str] = &[
"-x", "-x",
"--format=across", "--format=across",
"--format=acr",
"--format=horizontal", "--format=horizontal",
"--for=across", "--for=across",
"--for=horizontal", "--for=horizontal",
@ -999,6 +1001,8 @@ fn test_ls_zero() {
let ignored_opts = [ let ignored_opts = [
"--quoting-style=c", "--quoting-style=c",
"--color=always", "--color=always",
"--color=alway",
"--color=al",
"-m", "-m",
"--hide-control-chars", "--hide-control-chars",
]; ];
@ -1601,6 +1605,24 @@ fn test_ls_deref() {
.succeeds(); .succeeds();
assert!(re.is_match(result.stdout_str().trim())); assert!(re.is_match(result.stdout_str().trim()));
let result = scene
.ucmd()
.arg("-l")
.arg("--color=neve") // spell-checker:disable-line
.arg("test-long")
.arg("test-long.link")
.succeeds();
assert!(re.is_match(result.stdout_str().trim()));
let result = scene
.ucmd()
.arg("-l")
.arg("--color=n")
.arg("test-long")
.arg("test-long.link")
.succeeds();
assert!(re.is_match(result.stdout_str().trim()));
let result = scene let result = scene
.ucmd() .ucmd()
.arg("-L") .arg("-L")
@ -1676,6 +1698,10 @@ fn test_ls_sort_none() {
// Order is not specified so we just check that it doesn't // Order is not specified so we just check that it doesn't
// give any errors. // give any errors.
scene.ucmd().arg("--sort=none").succeeds(); scene.ucmd().arg("--sort=none").succeeds();
scene.ucmd().arg("--sort=non").succeeds();
scene.ucmd().arg("--sort=no").succeeds();
// scene.ucmd().arg("--sort=n").succeeds();
// We refuse to accept "--sort=n", since this is too confusable with "--sort=name", which is our own extension.
scene.ucmd().arg("-U").succeeds(); scene.ucmd().arg("-U").succeeds();
} }
@ -1693,6 +1719,16 @@ fn test_ls_sort_name() {
.arg("--sort=name") .arg("--sort=name")
.succeeds() .succeeds()
.stdout_is("test-1\ntest-2\ntest-3\n"); .stdout_is("test-1\ntest-2\ntest-3\n");
scene
.ucmd()
.arg("--sort=nam")
.succeeds()
.stdout_is("test-1\ntest-2\ntest-3\n");
scene
.ucmd()
.arg("--sort=na")
.succeeds()
.stdout_is("test-1\ntest-2\ntest-3\n");
let scene_dot = TestScenario::new(util_name!()); let scene_dot = TestScenario::new(util_name!());
let at = &scene_dot.fixtures; let at = &scene_dot.fixtures;
@ -1729,6 +1765,16 @@ fn test_ls_sort_width() {
.arg("--sort=width") .arg("--sort=width")
.succeeds() .succeeds()
.stdout_is("d\nzz\nabc\nbbb\neee\ncccc\naaaaa\nbcdef\nfffff\n"); .stdout_is("d\nzz\nabc\nbbb\neee\ncccc\naaaaa\nbcdef\nfffff\n");
scene
.ucmd()
.arg("--sort=widt") // spell-checker:disable-line
.succeeds()
.stdout_is("d\nzz\nabc\nbbb\neee\ncccc\naaaaa\nbcdef\nfffff\n");
scene
.ucmd()
.arg("--sort=w")
.succeeds()
.stdout_is("d\nzz\nabc\nbbb\neee\ncccc\naaaaa\nbcdef\nfffff\n");
} }
#[test] #[test]
@ -1757,6 +1803,12 @@ fn test_ls_order_size() {
let result = scene.ucmd().arg("--sort=size").succeeds(); let result = scene.ucmd().arg("--sort=size").succeeds();
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n"); result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
let result = scene.ucmd().arg("--sort=siz").succeeds();
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
let result = scene.ucmd().arg("--sort=s").succeeds();
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
let result = scene.ucmd().arg("--sort=size").arg("-r").succeeds(); let result = scene.ucmd().arg("--sort=size").arg("-r").succeeds();
result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n"); result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n");
} }
@ -1961,7 +2013,14 @@ fn test_ls_order_time() {
// 3 was accessed last in the read // 3 was accessed last in the read
// So the order should be 2 3 4 1 // So the order should be 2 3 4 1
for arg in ["-u", "--time=atime", "--time=access", "--time=use"] { for arg in [
"-u",
"--time=atime",
"--time=atim", // spell-checker:disable-line
"--time=a",
"--time=access",
"--time=use",
] {
let result = scene.ucmd().arg("-t").arg(arg).succeeds(); let result = scene.ucmd().arg("-t").arg(arg).succeeds();
at.open("test-3").metadata().unwrap().accessed().unwrap(); at.open("test-3").metadata().unwrap().accessed().unwrap();
at.open("test-4").metadata().unwrap().accessed().unwrap(); at.open("test-4").metadata().unwrap().accessed().unwrap();
@ -2216,12 +2275,16 @@ fn test_ls_indicator_style() {
for opt in [ for opt in [
"--indicator-style=classify", "--indicator-style=classify",
"--ind=classify", "--ind=classify",
"--indicator-style=clas", // spell-checker:disable-line
"--indicator-style=c",
"--indicator-style=file-type", "--indicator-style=file-type",
"--ind=file-type", "--ind=file-type",
"--indicator-style=slash", "--indicator-style=slash",
"--ind=slash", "--ind=slash",
"--classify", "--classify",
"--classify=always", "--classify=always",
"--classify=alway", // spell-checker:disable-line
"--classify=al",
"--classify=yes", "--classify=yes",
"--classify=force", "--classify=force",
"--class", "--class",
@ -2236,10 +2299,13 @@ fn test_ls_indicator_style() {
// Classify, Indicator options should not contain any indicators when value is none. // Classify, Indicator options should not contain any indicators when value is none.
for opt in [ for opt in [
"--indicator-style=none", "--indicator-style=none",
"--indicator-style=n",
"--ind=none", "--ind=none",
"--classify=none", "--classify=none",
"--classify=never", "--classify=never",
"--classify=non",
"--classify=no", "--classify=no",
"--classify=n",
] { ] {
// Verify that there are no indicators for any of the file types. // Verify that there are no indicators for any of the file types.
scene scene
@ -2553,6 +2619,12 @@ fn test_ls_version_sort() {
expected expected
); );
let result = scene.ucmd().arg("-1").arg("--sort=v").succeeds();
assert_eq!(
result.stdout_str().split('\n').collect::<Vec<_>>(),
expected
);
let result = scene.ucmd().arg("-a1v").succeeds(); let result = scene.ucmd().arg("-a1v").succeeds();
expected.insert(expected.len() - 1, ".."); expected.insert(expected.len() - 1, "..");
expected.insert(0, "."); expected.insert(0, ".");
@ -2589,19 +2661,27 @@ fn test_ls_quoting_style() {
for (arg, correct) in [ for (arg, correct) in [
("--quoting-style=literal", "one?two"), ("--quoting-style=literal", "one?two"),
("--quoting-style=litera", "one?two"), // spell-checker:disable-line
("--quoting-style=li", "one?two"),
("-N", "one?two"), ("-N", "one?two"),
("--literal", "one?two"), ("--literal", "one?two"),
("--l", "one?two"), ("--l", "one?two"),
("--quoting-style=c", "\"one\\ntwo\""), ("--quoting-style=c", "\"one\\ntwo\""),
("--quoting-style=c-", "\"one\\ntwo\""),
("--quoting-style=c-maybe", "\"one\\ntwo\""),
("-Q", "\"one\\ntwo\""), ("-Q", "\"one\\ntwo\""),
("--quote-name", "\"one\\ntwo\""), ("--quote-name", "\"one\\ntwo\""),
("--quoting-style=escape", "one\\ntwo"), ("--quoting-style=escape", "one\\ntwo"),
("--quoting-style=escap", "one\\ntwo"), // spell-checker:disable-line
("-b", "one\\ntwo"), ("-b", "one\\ntwo"),
("--escape", "one\\ntwo"), ("--escape", "one\\ntwo"),
("--quoting-style=shell-escape", "'one'$'\\n''two'"), ("--quoting-style=shell-escape", "'one'$'\\n''two'"),
("--quoting-style=shell-escape-always", "'one'$'\\n''two'"), ("--quoting-style=shell-escape-always", "'one'$'\\n''two'"),
("--quoting-style=shell-escape-alway", "'one'$'\\n''two'"),
("--quoting-style=shell-escape-a", "'one'$'\\n''two'"),
("--quoting-style=shell", "one?two"), ("--quoting-style=shell", "one?two"),
("--quoting-style=shell-always", "'one?two'"), ("--quoting-style=shell-always", "'one?two'"),
("--quoting-style=shell-a", "'one?two'"),
] { ] {
scene scene
.ucmd() .ucmd()
@ -4244,11 +4324,18 @@ fn test_ls_hyperlink() {
.stdout_str() .stdout_str()
.contains(&format!("{path}{separator}{file}\x07{file}\x1b]8;;\x07"))); .contains(&format!("{path}{separator}{file}\x07{file}\x1b]8;;\x07")));
scene for argument in [
.ucmd() "--hyperlink=never",
.arg("--hyperlink=never") "--hyperlink=neve", // spell-checker:disable-line
.succeeds() "--hyperlink=ne", // spell-checker:disable-line
.stdout_is(format!("{file}\n")); "--hyperlink=n",
] {
scene
.ucmd()
.arg(argument)
.succeeds()
.stdout_is(format!("{file}\n"));
}
} }
// spell-checker: disable // spell-checker: disable