feat(unexpand): move from getopts to clap (#1883)

* feat: move unexpand to clap

* chore: allow muliple files

* test: add test fixture, test reading from a file

* test: fix typo on file name, add test for multiple inputs

* chore: use 'success()' instead of asserting

* chore: delete unused variables

* chore: use help instead of long_help, break long line
This commit is contained in:
Yagiz Degirmenci 2021-03-23 11:42:05 +03:00 committed by GitHub
parent 5e2e2e8ab6
commit 545fe7d887
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 66 deletions

View file

@ -15,7 +15,7 @@ edition = "2018"
path = "src/unexpand.rs"
[dependencies]
getopts = "0.2.18"
clap = "2.33"
unicode-width = "0.1.5"
uucore = { version=">=0.0.7", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }

View file

@ -11,7 +11,7 @@
#[macro_use]
extern crate uucore;
use clap::{App, Arg};
use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Stdout, Write};
use std::str::from_utf8;
@ -19,6 +19,9 @@ use unicode_width::UnicodeWidthChar;
static NAME: &str = "unexpand";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static USAGE: &str = "unexpand [OPTION]... [FILE]...";
static SUMMARY: &str = "Convert blanks in each FILE to tabs, writing to standard output.\n
With no FILE, or when FILE is -, read standard input.";
const DEFAULT_TABSTOP: usize = 8;
@ -46,6 +49,14 @@ fn tabstops_parse(s: String) -> Vec<usize> {
nums
}
mod options {
pub const FILE: &str = "file";
pub const ALL: &str = "all";
pub const FIRST_ONLY: &str = "first-only";
pub const TABS: &str = "tabs";
pub const NO_UTF8: &str = "no-utf8";
}
struct Options {
files: Vec<String>,
tabstops: Vec<usize>,
@ -54,20 +65,19 @@ struct Options {
}
impl Options {
fn new(matches: getopts::Matches) -> Options {
let tabstops = match matches.opt_str("t") {
fn new(matches: clap::ArgMatches) -> Options {
let tabstops = match matches.value_of(options::TABS) {
None => vec![DEFAULT_TABSTOP],
Some(s) => tabstops_parse(s),
Some(s) => tabstops_parse(s.to_string()),
};
let aflag = (matches.opt_present("all") || matches.opt_present("tabs"))
&& !matches.opt_present("first-only");
let uflag = !matches.opt_present("U");
let aflag = (matches.is_present(options::ALL) || matches.is_present(options::TABS))
&& !matches.is_present(options::FIRST_ONLY);
let uflag = !matches.is_present(options::NO_UTF8);
let files = if matches.free.is_empty() {
vec!["-".to_owned()]
} else {
matches.free
let files = match matches.value_of(options::FILE) {
Some(v) => vec![v.to_string()],
None => vec!["-".to_owned()],
};
Options {
@ -82,60 +92,39 @@ impl Options {
pub fn uumain(args: impl uucore::Args) -> i32 {
let args = args.collect_str();
let mut opts = getopts::Options::new();
opts.optflag(
"a",
"all",
"convert all blanks, instead of just initial blanks",
);
opts.optflag(
"",
"first-only",
"convert only leading sequences of blanks (overrides -a)",
);
opts.optopt(
"t",
"tabs",
"have tabs N characters apart instead of 8 (enables -a)",
"N",
);
opts.optopt(
"t",
"tabs",
"use comma separated LIST of tab positions (enables -a)",
"LIST",
);
opts.optflag(
"U",
"no-utf8",
"interpret input file as 8-bit ASCII rather than UTF-8",
);
opts.optflag("h", "help", "display this help and exit");
opts.optflag("V", "version", "output version information and exit");
let matches = match opts.parse(&args[1..]) {
Ok(m) => m,
Err(f) => crash!(1, "{}", f),
};
if matches.opt_present("help") {
println!("{} {}\n", NAME, VERSION);
println!("Usage: {} [OPTION]... [FILE]...\n", NAME);
println!(
"{}",
opts.usage(
"Convert blanks in each FILE to tabs, writing to standard output.\n\
With no FILE, or when FILE is -, read standard input."
)
);
return 0;
}
if matches.opt_present("V") {
println!("{} {}", NAME, VERSION);
return 0;
}
let matches = App::new(executable!())
.name(NAME)
.version(VERSION)
.usage(USAGE)
.about(SUMMARY)
.arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
.arg(
Arg::with_name(options::ALL)
.short("a")
.long(options::ALL)
.help("convert all blanks, instead of just initial blanks")
.takes_value(false),
)
.arg(
Arg::with_name(options::FIRST_ONLY)
.long(options::FIRST_ONLY)
.help("convert only leading sequences of blanks (overrides -a)")
.takes_value(false),
)
.arg(
Arg::with_name(options::TABS)
.short("t")
.long(options::TABS)
.long_help("use comma separated LIST of tab positions or have tabs N characters apart instead of 8 (enables -a)")
.takes_value(true)
)
.arg(
Arg::with_name(options::NO_UTF8)
.short("U")
.long(options::NO_UTF8)
.takes_value(false)
.help("interpret input file as 8-bit ASCII rather than UTF-8"))
.get_matches_from(args);
unexpand(Options::new(matches));

View file

@ -136,3 +136,22 @@ fn unexpand_spaces_after_fields() {
.run()
.stdout_is("\t\tA B C D\t\t A\t\n");
}
#[test]
fn unexpand_read_from_file() {
new_ucmd!()
.arg("with_spaces.txt")
.arg("-t4")
.run()
.success();
}
#[test]
fn unexpand_read_from_two_file() {
new_ucmd!()
.arg("with_spaces.txt")
.arg("with_spaces.txt")
.arg("-t4")
.run()
.success();
}

View file

@ -0,0 +1,2 @@
abc d e f g \t\t A