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

unexpand: implement "tabs" shortcuts

This commit is contained in:
Daniel Hofstetter 2022-06-17 12:59:02 +02:00
parent fa51f8b986
commit edf4fee48f
2 changed files with 88 additions and 4 deletions

View file

@ -7,7 +7,7 @@
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (ToDO) nums aflag uflag scol prevtab amode ctype cwidth nbytes lastcol pctype
// spell-checker:ignore (ToDO) nums aflag uflag scol prevtab amode ctype cwidth nbytes lastcol pctype Preprocess
#[macro_use]
extern crate uucore;
@ -97,9 +97,9 @@ struct Options {
impl Options {
fn new(matches: &clap::ArgMatches) -> Result<Self, ParseError> {
let tabstops = match matches.value_of(options::TABS) {
let tabstops = match matches.values_of(options::TABS) {
None => vec![DEFAULT_TABSTOP],
Some(s) => tabstops_parse(s)?,
Some(s) => tabstops_parse(&s.collect::<Vec<&str>>().join(","))?,
};
let aflag = (matches.is_present(options::ALL) || matches.is_present(options::TABS))
@ -120,13 +120,49 @@ impl Options {
}
}
/// Decide whether the character is either a digit or a comma.
fn is_digit_or_comma(c: char) -> bool {
c.is_ascii_digit() || c == ','
}
/// Preprocess command line arguments and expand shortcuts. For example, "-7" is expanded to
/// "--tabs=7 --first-only" and "-1,3" to "--tabs=1 --tabs=3 --first-only". However, if "-a" or
/// "--all" is provided, "--first-only" is omitted.
fn expand_shortcuts(args: &[String]) -> Vec<String> {
let mut processed_args = Vec::with_capacity(args.len());
let mut is_all_arg_provided = false;
let mut has_shortcuts = false;
for arg in args {
if arg.starts_with('-') && arg[1..].chars().all(is_digit_or_comma) {
arg[1..]
.split(',')
.filter(|s| !s.is_empty())
.for_each(|s| processed_args.push(format!("--tabs={}", s)));
has_shortcuts = true;
} else {
processed_args.push(arg.to_string());
if arg == "--all" || arg == "-a" {
is_all_arg_provided = true;
}
}
}
if has_shortcuts && !is_all_arg_provided {
processed_args.push("--first-only".into());
}
processed_args
}
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(InvalidEncodingHandling::Ignore)
.accept_any();
let matches = uu_app().get_matches_from(args);
let matches = uu_app().get_matches_from(expand_shortcuts(&args));
unexpand(&Options::new(&matches)?).map_err_context(String::new)
}
@ -163,6 +199,7 @@ pub fn uu_app<'a>() -> Command<'a> {
.long(options::TABS)
.help("use comma separated LIST of tab positions or have tabs N characters apart instead of 8 (enables -a)")
.takes_value(true)
.multiple_occurrences(true)
.value_name("N, LIST")
)
.arg(
@ -379,3 +416,15 @@ fn unexpand(options: &Options) -> std::io::Result<()> {
}
output.flush()
}
#[cfg(test)]
mod tests {
use crate::is_digit_or_comma;
#[test]
fn test_is_digit_or_comma() {
assert!(is_digit_or_comma('1'));
assert!(is_digit_or_comma(','));
assert!(!is_digit_or_comma('a'));
}
}

View file

@ -156,6 +156,41 @@ fn unexpand_read_from_two_file() {
.success();
}
#[test]
fn test_tabs_shortcut() {
new_ucmd!()
.arg("-3")
.pipe_in(" a b")
.run()
.stdout_is("\ta b");
}
#[test]
fn test_tabs_shortcut_combined_with_all_arg() {
fn run_cmd(all_arg: &str) {
new_ucmd!()
.args(&[all_arg, "-3"])
.pipe_in("a b c")
.run()
.stdout_is("a\tb\tc");
}
let all_args = vec!["-a", "--all"];
for arg in all_args {
run_cmd(arg);
}
}
#[test]
fn test_comma_separated_tabs_shortcut() {
new_ucmd!()
.args(&["-a", "-3,9"])
.pipe_in("a b c")
.run()
.stdout_is("a\tb\tc");
}
#[test]
fn test_tabs_cannot_be_zero() {
new_ucmd!()