From cfc3d52be40958d7042c0f56368212a41c0e8dcf Mon Sep 17 00:00:00 2001 From: Yagiz Degirmenci Date: Sat, 3 Apr 2021 20:19:30 +0300 Subject: [PATCH] cut: move to clap --- Cargo.lock | 1 + src/uu/cut/Cargo.toml | 1 + src/uu/cut/src/cut.rs | 150 ++++++++++++++++++++++++++++++++++-------- 3 files changed, 124 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea1ee53ae..15f1a606d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1679,6 +1679,7 @@ dependencies = [ name = "uu_cut" version = "0.0.6" dependencies = [ + "clap", "uucore", "uucore_procs", ] diff --git a/src/uu/cut/Cargo.toml b/src/uu/cut/Cargo.toml index 9cc852d2e..d892ddeb5 100644 --- a/src/uu/cut/Cargo.toml +++ b/src/uu/cut/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" path = "src/cut.rs" [dependencies] +clap = "2.33" uucore = { version=">=0.0.8", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/cut/src/cut.rs b/src/uu/cut/src/cut.rs index 95411e3fb..2b65a3347 100644 --- a/src/uu/cut/src/cut.rs +++ b/src/uu/cut/src/cut.rs @@ -10,6 +10,7 @@ #[macro_use] extern crate uucore; +use clap::{App, Arg}; use std::fs::File; use std::io::{stdin, stdout, BufRead, BufReader, Read, Stdout, Write}; use std::path::Path; @@ -20,6 +21,8 @@ use uucore::ranges::Range; mod buffer; mod searcher; +static NAME: &str = "cut"; +static VERSION: &str = env!("CARGO_PKG_VERSION"); static SYNTAX: &str = "[-d] [-s] [-z] [--output-delimiter] ((-f|-b|-c) {{sequence}}) {{sourcefile}}+"; static SUMMARY: &str = @@ -422,34 +425,119 @@ fn cut_files(mut filenames: Vec, mode: Mode) -> i32 { exit_code } +mod options { + pub const BYTES: &str = "bytes"; + pub const CHARACTERS: &str = "characters"; + pub const DELIMITER: &str = "delimiter"; + pub const FIELDS: &str = "fields"; + pub const LEGACY_OPTION: &str = "legacy-option"; + pub const ZERO_TERMINATED: &str = "zero-terminated"; + pub const ONLY_DELIMITED: &str = "only-delimited"; + pub const OUTPUT_DELIMITER: &str = "output-delimiter"; + pub const COMPLEMENT: &str = "complement"; + pub const FILE: &str = "file"; +} + pub fn uumain(args: impl uucore::Args) -> i32 { let args = args.collect_str(); - let matches = app!(SYNTAX, SUMMARY, LONG_HELP) - .optopt("b", "bytes", "filter byte columns from the input source", "sequence") - .optopt("c", "characters", "alias for character mode", "sequence") - .optopt("d", "delimiter", "specify the delimiter character that separates fields in the input source. Defaults to Tab.", "delimiter") - .optopt("f", "fields", "filter field columns from the input source", "sequence") - .optflag("n", "", "legacy option - has no effect.") - .optflag("", "complement", "invert the filter - instead of displaying only the filtered columns, display all but those columns") - .optflag("s", "only-delimited", "in field mode, only print lines which contain the delimiter") - .optflag("z", "zero-terminated", "instead of filtering columns based on line, filter columns based on \\0 (NULL character)") - .optopt("", "output-delimiter", "in field mode, replace the delimiter in output lines with this option's argument", "new delimiter") - .parse(args); - let complement = matches.opt_present("complement"); + let matches = App::new(executable!()) + .name(NAME) + .version(VERSION) + .usage(SYNTAX) + .about(SUMMARY) + .after_help(LONG_HELP) + .arg( + Arg::with_name(options::BYTES) + .short("b") + .long(options::BYTES) + .takes_value(true) + .help("filter byte columns from the input source") + .allow_hyphen_values(true) + .value_name("LIST"), + ) + .arg( + Arg::with_name(options::CHARACTERS) + .short("c") + .long(options::CHARACTERS) + .help("alias for character mode") + .takes_value(true) + .allow_hyphen_values(true) + .value_name("LIST"), + ) + .arg( + Arg::with_name(options::DELIMITER) + .short("d") + .long(options::DELIMITER) + .help("specify the delimiter character that separates fields in the input source. Defaults to Tab.") + .takes_value(true) + .value_name("DELIM"), + ) + .arg( + Arg::with_name(options::FIELDS) + .short("f") + .long(options::FIELDS) + .help("filter field columns from the input source") + .takes_value(true) + .allow_hyphen_values(true) + .value_name("LIST"), + ) + .arg( + Arg::with_name(options::LEGACY_OPTION) + .short("n") + .long(options::LEGACY_OPTION) + .help("legacy option - has no effect.") + .takes_value(false) + ) + .arg( + Arg::with_name(options::COMPLEMENT) + .long(options::COMPLEMENT) + .help("invert the filter - instead of displaying only the filtered columns, display all but those columns") + .takes_value(false) + + ) + .arg( + Arg::with_name(options::ONLY_DELIMITED) + .short("s") + .long(options::ONLY_DELIMITED) + .help("in field mode, only print lines which contain the delimiter") + .takes_value(false) + ) + .arg( + Arg::with_name(options::ZERO_TERMINATED) + .short("z") + .long(options::ZERO_TERMINATED) + .help("instead of filtering columns based on line, filter columns based on \\0 (NULL character)") + .takes_value(false) + ) + .arg( + Arg::with_name(options::OUTPUT_DELIMITER) + .long(options::OUTPUT_DELIMITER) + .help("in field mode, replace the delimiter in output lines with this option's argument") + .takes_value(true) + .value_name("NEW_DELIM") + ) + .arg( + Arg::with_name(options::FILE) + .hidden(true) + .multiple(true) + ) + .get_matches_from(args); + + let complement = matches.is_present("complement"); let mode_parse = match ( - matches.opt_str("bytes"), - matches.opt_str("characters"), - matches.opt_str("fields"), + matches.value_of(options::BYTES), + matches.value_of(options::CHARACTERS), + matches.value_of(options::FIELDS), ) { (Some(byte_ranges), None, None) => { list_to_ranges(&byte_ranges[..], complement).map(|ranges| { Mode::Bytes( ranges, Options { - out_delim: matches.opt_str("output-delimiter"), - zero_terminated: matches.opt_present("zero-terminated"), + out_delim: Some(matches.value_of(options::OUTPUT_DELIMITER).unwrap_or_default().to_owned()), + zero_terminated: matches.is_present(options::ZERO_TERMINATED), }, ) }) @@ -459,29 +547,29 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Mode::Characters( ranges, Options { - out_delim: matches.opt_str("output-delimiter"), - zero_terminated: matches.opt_present("zero-terminated"), + out_delim: Some(matches.value_of(options::OUTPUT_DELIMITER).unwrap_or_default().to_owned()), + zero_terminated: matches.is_present(options::ZERO_TERMINATED), }, ) }) } (None, None, Some(field_ranges)) => { list_to_ranges(&field_ranges[..], complement).and_then(|ranges| { - let out_delim = match matches.opt_str("output-delimiter") { + let out_delim = match matches.value_of(options::OUTPUT_DELIMITER) { Some(s) => { if s.is_empty() { Some("\0".to_owned()) } else { - Some(s) + Some(s.to_owned()) } } None => None, }; - let only_delimited = matches.opt_present("only-delimited"); - let zero_terminated = matches.opt_present("zero-terminated"); + let only_delimited = matches.is_present(options::ONLY_DELIMITED); + let zero_terminated = matches.is_present(options::ZERO_TERMINATED); - match matches.opt_str("delimiter") { + match matches.value_of(options::DELIMITER) { Some(delim) => { if delim.chars().count() > 1 { Err(msg_opt_invalid_should_be!( @@ -494,7 +582,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let delim = if delim.is_empty() { "\0".to_owned() } else { - delim + delim.to_owned() }; Ok(Mode::Fields( @@ -533,10 +621,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let mode_parse = match mode_parse { Err(_) => mode_parse, Ok(mode) => match mode { - Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.opt_present("delimiter") => Err( + Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.is_present("delimiter") => Err( msg_opt_only_usable_if!("printing a sequence of fields", "--delimiter", "-d"), ), - Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.opt_present("only-delimited") => { + Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.is_present("only-delimited") => { Err(msg_opt_only_usable_if!( "printing a sequence of fields", "--only-delimited", @@ -547,8 +635,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 { }, }; + let files: Vec = matches + .values_of(options::FILE) + .unwrap_or_default() + .map(str::to_owned) + .collect(); + match mode_parse { - Ok(mode) => cut_files(matches.free, mode), + Ok(mode) => cut_files(files, mode), Err(err_msg) => { show_error!("{}", err_msg); 1