From 5578ba6eed000249122627099303cbb6c7db52d2 Mon Sep 17 00:00:00 2001 From: Ricardo Iglesias Date: Sun, 25 Apr 2021 22:24:55 -0700 Subject: [PATCH] base32: move from getopts to clap Note, I needed to change the error messages in one of the tests because getopt and clap have different error messages when not providing a default value --- Cargo.lock | 3 + src/uu/base32/Cargo.toml | 1 + src/uu/base32/src/base32.rs | 9 +- src/uu/base32/src/base_common.rs | 204 ++++++++++++++++++++++++------- tests/by-util/test_base32.rs | 21 +++- 5 files changed, 182 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 321f41d89..aea4956e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "advapi32-sys" version = "0.2.0" @@ -1653,6 +1655,7 @@ dependencies = [ name = "uu_base32" version = "0.0.6" dependencies = [ + "clap", "uucore", "uucore_procs", ] diff --git a/src/uu/base32/Cargo.toml b/src/uu/base32/Cargo.toml index a1d7ba17e..1b448af0a 100644 --- a/src/uu/base32/Cargo.toml +++ b/src/uu/base32/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" path = "src/base32.rs" [dependencies] +clap = "2.33" uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features = ["encoding"] } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/base32/src/base32.rs b/src/uu/base32/src/base32.rs index b47f4d4cc..b11b02da7 100644 --- a/src/uu/base32/src/base32.rs +++ b/src/uu/base32/src/base32.rs @@ -11,7 +11,6 @@ use uucore::encoding::Format; mod base_common; -static SYNTAX: &str = "[OPTION]... [FILE]"; static SUMMARY: &str = "Base32 encode or decode FILE, or standard input, to standard output."; static LONG_HELP: &str = " With no FILE, or when FILE is -, read standard input. @@ -24,11 +23,5 @@ static LONG_HELP: &str = " "; pub fn uumain(args: impl uucore::Args) -> i32 { - base_common::execute( - args.collect_str(), - SYNTAX, - SUMMARY, - LONG_HELP, - Format::Base32, - ) + base_common::execute(args.collect_str(), SUMMARY, LONG_HELP, Format::Base32) } diff --git a/src/uu/base32/src/base_common.rs b/src/uu/base32/src/base_common.rs index 3f1436fb2..136a54425 100644 --- a/src/uu/base32/src/base_common.rs +++ b/src/uu/base32/src/base_common.rs @@ -11,60 +11,172 @@ use std::fs::File; use std::io::{stdin, stdout, BufReader, Read, Write}; use std::path::Path; +use clap::{App, Arg}; use uucore::encoding::{wrap_print, Data, Format}; -pub fn execute( - args: Vec, - syntax: &str, - summary: &str, - long_help: &str, - format: Format, -) -> i32 { - let matches = app!(syntax, summary, long_help) - .optflag("d", "decode", "decode data") - .optflag( - "i", - "ignore-garbage", - "when decoding, ignore non-alphabetic characters", - ) - .optopt( - "w", - "wrap", - "wrap encoded lines after COLS character (default 76, 0 to disable wrapping)", - "COLS", - ) - .parse(args); +static VERSION: &str = env!("CARGO_PKG_VERSION"); - let line_wrap = matches.opt_str("wrap").map(|s| match s.parse() { - Ok(n) => n, - Err(e) => { - crash!(1, "invalid wrap size: ‘{}’: {}", s, e); +pub mod options { + pub static DECODE: &str = "decode"; + pub static WRAP: &str = "wrap"; + pub static IGNORE_GARBAGE: &str = "ignore-garbage"; + pub static FILE: &str = "file"; +} + +struct Config { + decode: bool, + ignore_garbage: bool, + wrap_cols: Option, + to_read: Option, +} + +impl Config { + fn from(options: clap::ArgMatches) -> Config { + let file: Option = match options.values_of(options::FILE) { + Some(mut values) => { + if values.len() != 1 { + crash!(3, "extra operand ‘{}’", values.nth(0).unwrap()) + } + let name = values.nth(0).unwrap(); + if !Path::exists(Path::new(name)) { + crash!(2, "{}: No such file or directory", name); + } + + if name == "-" { + None // stdin + } else { + Some(name.to_owned()) + } + } + None => None, + }; + + let cols = match options.value_of(options::WRAP) { + Some(num) => match num.parse::() { + Ok(n) => Some(n), + Err(e) => { + crash!(1, "invalid wrap size: ‘{}’: {}", num, e); + } + }, + None => None, + }; + + Config { + decode: options.is_present(options::DECODE), + ignore_garbage: options.is_present(options::IGNORE_GARBAGE), + wrap_cols: cols, + to_read: file, } - }); - let ignore_garbage = matches.opt_present("ignore-garbage"); - let decode = matches.opt_present("decode"); - - if matches.free.len() > 1 { - show_usage_error!("extra operand ‘{}’", matches.free[0]); - return 1; } +} - if matches.free.is_empty() || &matches.free[0][..] == "-" { - let stdin_raw = stdin(); - handle_input( - &mut stdin_raw.lock(), - format, - line_wrap, - ignore_garbage, - decode, - ); - } else { - let path = Path::new(matches.free[0].as_str()); - let file_buf = safe_unwrap!(File::open(&path)); - let mut input = BufReader::new(file_buf); - handle_input(&mut input, format, line_wrap, ignore_garbage, decode); +fn get_usage() -> String { + format!("{0} [OPTION]... [FILE]", executable!()) +} + +pub fn execute(args: Vec, _summary: &str, long_help: &str, format: Format) -> i32 { + let usage = get_usage(); + let app = App::new(executable!()) + .version(VERSION) + .usage(&usage[..]) + .about(long_help) + // Format arguments. + .arg( + Arg::with_name(options::DECODE) + .short("d") + .long(options::DECODE) + .help("decode data"), + ) + .arg( + Arg::with_name(options::IGNORE_GARBAGE) + .short("i") + .long(options::IGNORE_GARBAGE) + .help("when decoding, ignore non-alphabetic characters"), + ) + .arg( + Arg::with_name(options::WRAP) + .short("w") + .long(options::WRAP) + .takes_value(true) + .help( + "wrap encoded lines after COLS character (default 76, 0 to disable wrapping)", + ), + ) + // "multiple" arguments are used to check whether there is more than one + // file passed in. + .arg(Arg::with_name(options::FILE).index(1).multiple(true)); + + let config: Config = Config::from(app.get_matches_from(args)); + match config.to_read { + // Read from file. + Some(name) => { + let file_buf = safe_unwrap!(File::open(Path::new(&name))); + let mut input = BufReader::new(file_buf); + handle_input( + &mut input, + format, + config.wrap_cols, + config.ignore_garbage, + config.decode, + ); + } + // stdin + None => { + handle_input( + &mut stdin().lock(), + format, + config.wrap_cols, + config.ignore_garbage, + config.decode, + ); + } }; + // let matches = app!(syntax, summary, long_help) + // .optflag("d", "decode", "decode data") + // .optflag( + // "i", + // "ignore-garbage", + // "when decoding, ignore non-alphabetic characters", + // ) + // .optopt( + // "w", + // "wrap", + // "wrap encoded lines after COLS character (default 76, 0 to disable wrapping)", + // "COLS", + // ) + // .parse(args); + + // let line_wrap = matches.opt_str("wrap").map(|s| match s.parse() { + // Ok(n) => n, + // Err(e) => { + // crash!(1, "invalid wrap size: ‘{}’: {}", s, e); + // } + // }); + // let ignore_garbage = matches.opt_present("ignore-garbage"); + // let decode = matches.opt_present("decode"); + + // if matches.free.len() > 1 { + // show_usage_error!("extra operand ‘{}’", matches.free[0]); + // return 1; + // } + + // if matches.free.is_empty() || &matches.free[0][..] == "-" { + // let stdin_raw = stdin(); + // handle_input( + // &mut stdin_raw.lock(), + // format, + // line_wrap, + // ignore_garbage, + // decode, + // ); + // } else { + // let path = Path::new(matches.free[0].as_str()); + // let file_buf = safe_unwrap!(File::open(&path)); + // let mut input = BufReader::new(file_buf); + // + // }; + 0 } diff --git a/tests/by-util/test_base32.rs b/tests/by-util/test_base32.rs index d3527d26a..68e6b5050 100644 --- a/tests/by-util/test_base32.rs +++ b/tests/by-util/test_base32.rs @@ -71,8 +71,7 @@ fn test_wrap() { fn test_wrap_no_arg() { for wrap_param in vec!["-w", "--wrap"] { new_ucmd!().arg(wrap_param).fails().stderr_only(format!( - "base32: error: Argument to option '{}' missing\n", - if wrap_param == "-w" { "w" } else { "wrap" } + "error: The argument '--wrap \' requires a value but none was supplied\n\nUSAGE:\n base32 [OPTION]... [FILE]\n\nFor more information try --help" )); } } @@ -87,3 +86,21 @@ fn test_wrap_bad_arg() { .stderr_only("base32: error: invalid wrap size: ‘b’: invalid digit found in string\n"); } } + +#[test] +fn test_base32_extra_operand() { + // Expect a failure when multiple files are specified. + new_ucmd!() + .arg("a.txt") + .arg("a.txt") + .fails() + .stderr_only("base32: error: extra operand ‘a.txt’"); +} + +#[test] +fn test_base32_file_not_found() { + new_ucmd!() + .arg("a.txt") + .fails() + .stderr_only("base32: error: a.txt: No such file or directory"); +}