diff --git a/src/uu/cut/locales/en-US.ftl b/src/uu/cut/locales/en-US.ftl index 6e63feb2638..cbaeee5b4a2 100644 --- a/src/uu/cut/locales/en-US.ftl +++ b/src/uu/cut/locales/en-US.ftl @@ -48,6 +48,14 @@ cut-error-invalid-field-value = invalid field value { $value } cut-error-invalid-position-value = invalid byte/character position { $value } cut-error-field-number-too-large = field number { $value } is too large cut-error-position-too-large = byte/character offset { $value } is too large +cut-error-invalid-whitespace-delimited-choice = invalid argument '{ $arg }' for '--whitespace-delimited' + Valid arguments are: + { $choices } + Try 'cut --help' for more information. +cut-error-ambiguous-whitespace-delimited-choice = ambiguous argument '{ $arg }' for '--whitespace-delimited' + Valid arguments are: + { $choices } + Try 'cut --help' for more information. # Diagnostic labels: what the caret points at in a list of ranges cut-diag-label-zero-bound = counting starts at 1 diff --git a/src/uu/cut/locales/fr-FR.ftl b/src/uu/cut/locales/fr-FR.ftl index 8428bb9c0fc..d60b28649b2 100644 --- a/src/uu/cut/locales/fr-FR.ftl +++ b/src/uu/cut/locales/fr-FR.ftl @@ -123,6 +123,14 @@ cut-error-invalid-field-value = valeur de champ invalide { $value } cut-error-invalid-position-value = position d'octet/caractère invalide { $value } cut-error-field-number-too-large = le numéro de champ { $value } est trop grand cut-error-position-too-large = le décalage d'octet/caractère { $value } est trop grand +cut-error-invalid-whitespace-delimited-choice = argument '{ $arg }' invalide pour '--whitespace-delimited' + Les arguments valides sont : + { $choices } + Essayez 'cut --help' pour plus d'informations. +cut-error-ambiguous-whitespace-delimited-choice = argument '{ $arg }' ambigu pour '--whitespace-delimited' + Les arguments valides sont : + { $choices } + Essayez 'cut --help' pour plus d'informations. # Étiquettes de diagnostic : ce que le caret désigne dans une liste d'intervalles cut-diag-label-zero-bound = le décompte commence à 1 diff --git a/src/uu/cut/src/cut.rs b/src/uu/cut/src/cut.rs index 8d31ae493af..f405af538e3 100644 --- a/src/uu/cut/src/cut.rs +++ b/src/uu/cut/src/cut.rs @@ -6,7 +6,7 @@ // spell-checker:ignore (ToDO) delim foxjumping sourcefiles undelimited xacfoxjumping use bstr::io::BufReadExt; -use clap::builder::{PossibleValue, ValueParser}; +use clap::builder::ValueParser; use clap::{Arg, ArgAction, ArgMatches, Command}; use std::ffi::OsString; use std::fs::File; @@ -17,7 +17,6 @@ use uucore::error::{FromIo, UResult, USimpleError, UUsageError, set_exit_code, s use uucore::i18n::charmap::{Encoding, locale_encoding, mb_char_len}; use uucore::line_ending::LineEnding; use uucore::os_str_as_bytes; -use uucore::parser::shortcut_value_parser::ShortcutValueParser; use self::searcher::Searcher; use matcher::{ExactMatcher, Matcher, MbExactMatcher, WhitespaceMatcher}; @@ -968,6 +967,37 @@ where ); } +/// The choices `--whitespace-delimited`'s value accepts. +const WHITESPACE_DELIMITED_CHOICES: &[&str] = &["trimmed"]; + +/// The choice `value` names among `WHITESPACE_DELIMITED_CHOICES`, accepting +/// any unambiguous abbreviation the way GNU does -- including the empty +/// string, which (like everywhere else here) is a prefix of every choice +/// but, with only one choice to begin with, names it unambiguously. +fn resolve_whitespace_delimited_choice(value: &str) -> UResult<&'static str> { + let list = || { + WHITESPACE_DELIMITED_CHOICES + .iter() + .map(|name| format!(" - '{name}'")) + .collect::>() + .join("\n") + }; + let mut named = WHITESPACE_DELIMITED_CHOICES + .iter() + .filter(|name| name.starts_with(value)); + match (named.next(), named.next()) { + (Some(name), None) => Ok(*name), + (Some(_), Some(_)) => Err(USimpleError::new( + 1, + translate!("cut-error-ambiguous-whitespace-delimited-choice", "arg" => value.to_string(), "choices" => list()), + )), + _ => Err(USimpleError::new( + 1, + translate!("cut-error-invalid-whitespace-delimited-choice", "arg" => value.to_string(), "choices" => list()), + )), + } +} + /// Get delimiter and output delimiter from `-d`/`--delimiter` and `--output-delimiter` options respectively /// Allow either delimiter to have a value that is neither UTF-8 nor ASCII to align with GNU behavior fn get_delimiters(matches: &ArgMatches) -> UResult<(Delimiter<'_>, Option<&[u8]>)> { @@ -1065,6 +1095,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { // `--whitespace-delimited[=trimmed]` (`-w`): the optional value selects trimming. let whitespace_trimmed = matches .get_one::(options::WHITESPACE_DELIMITED) + .map(|value| resolve_whitespace_delimited_choice(value)) + .transpose()? .is_some(); let mode_arg = get_mode_arg(&matches)?; @@ -1276,7 +1308,6 @@ pub fn uu_app() -> Command { .long(options::WHITESPACE_DELIMITED) .help(translate!("cut-help-whitespace-delimited")) .value_name("trimmed") - .value_parser(ShortcutValueParser::new([PossibleValue::new("trimmed")])) .num_args(0..=1) .require_equals(true) .action(ArgAction::Set), diff --git a/tests/by-util/test_cut.rs b/tests/by-util/test_cut.rs index 9c2c7b3c3e6..2cd31ad7d03 100644 --- a/tests/by-util/test_cut.rs +++ b/tests/by-util/test_cut.rs @@ -746,7 +746,13 @@ fn test_whitespace_delimited_long_and_trimmed() { // Only `trimmed` is a valid value. new_ucmd!() .args(&["--whitespace-delimited=middle", "-f1"]) - .fails_with_code(1); + .fails_with_code(1) + .stderr_is(concat!( + "cut: invalid argument 'middle' for '--whitespace-delimited'\n", + "Valid arguments are:\n", + " - 'trimmed'\n", + "Try 'cut --help' for more information.\n", + )); } #[test]