1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-30 12:37:49 +00:00

numfmt: replace getopts with clap (#1717)

This commit is contained in:
Daniel Rocco 2021-02-11 17:58:26 -05:00 committed by GitHub
parent 35a7f01d15
commit 9081e120d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 110 additions and 101 deletions

View file

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

View file

@ -5,12 +5,50 @@
// * For the full copyright and license information, please view the LICENSE // * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code. // * file that was distributed with this source code.
use getopts::{Matches, Options};
use std::fmt; use std::fmt;
use std::io::BufRead; use std::io::BufRead;
static NAME: &str = "numfmt"; #[macro_use]
extern crate uucore;
use clap::{App, Arg, ArgMatches};
static VERSION: &str = env!("CARGO_PKG_VERSION"); static VERSION: &str = env!("CARGO_PKG_VERSION");
static ABOUT: &str = "Convert numbers from/to human-readable strings";
static LONG_HELP: &str = "UNIT options:
none no auto-scaling is done; suffixes will trigger an error
auto accept optional single/two letter suffix:
1K = 1000, 1Ki = 1024, 1M = 1000000, 1Mi = 1048576,
si accept optional single letter suffix:
1K = 1000, 1M = 1000000, ...
iec accept optional single letter suffix:
1K = 1024, 1M = 1048576, ...
iec-i accept optional two-letter suffix:
1Ki = 1024, 1Mi = 1048576, ...
";
mod options {
pub const FROM: &str = "from";
pub const FROM_DEFAULT: &str = "none";
pub const HEADER: &str = "header";
pub const HEADER_DEFAULT: &str = "1";
pub const NUMBER: &str = "NUMBER";
pub const PADDING: &str = "padding";
pub const TO: &str = "to";
pub const TO_DEFAULT: &str = "none";
}
fn get_usage() -> String {
format!("{0} [OPTION]... [NUMBER]...", executable!())
}
const IEC_BASES: [f64; 10] = [ const IEC_BASES: [f64; 10] = [
//premature optimization //premature optimization
@ -72,7 +110,7 @@ impl fmt::Display for DisplayableSuffix {
} }
} }
fn parse_suffix(s: String) -> Result<(f64, Option<Suffix>)> { fn parse_suffix(s: &str) -> Result<(f64, Option<Suffix>)> {
let with_i = s.ends_with('i'); let with_i = s.ends_with('i');
let mut iter = s.chars(); let mut iter = s.chars();
if with_i { if with_i {
@ -104,8 +142,8 @@ fn parse_suffix(s: String) -> Result<(f64, Option<Suffix>)> {
Ok((number, suffix)) Ok((number, suffix))
} }
fn parse_unit(s: String) -> Result<Unit> { fn parse_unit(s: &str) -> Result<Unit> {
match &s[..] { match s {
"auto" => Ok(Unit::Auto), "auto" => Ok(Unit::Auto),
"si" => Ok(Unit::Si), "si" => Ok(Unit::Si),
"iec" => Ok(Unit::Iec(false)), "iec" => Ok(Unit::Iec(false)),
@ -161,7 +199,7 @@ fn remove_suffix(i: f64, s: Option<Suffix>, u: &Unit) -> Result<f64> {
} }
} }
fn transform_from(s: String, opts: &Transform) -> Result<f64> { fn transform_from(s: &str, opts: &Transform) -> Result<f64> {
let (i, suffix) = parse_suffix(s)?; let (i, suffix) = parse_suffix(s)?;
remove_suffix(i, suffix, &opts.unit).map(|n| n.round()) remove_suffix(i, suffix, &opts.unit).map(|n| n.round())
} }
@ -206,7 +244,7 @@ fn transform_to(s: f64, opts: &Transform) -> Result<String> {
}) })
} }
fn format_string(source: String, options: &NumfmtOptions) -> Result<String> { fn format_string(source: &str, options: &NumfmtOptions) -> Result<String> {
let number = transform_to( let number = transform_to(
transform_from(source, &options.transform.from)?, transform_from(source, &options.transform.from)?,
&options.transform.to, &options.transform.to,
@ -219,30 +257,27 @@ fn format_string(source: String, options: &NumfmtOptions) -> Result<String> {
}) })
} }
fn parse_options(args: &Matches) -> Result<NumfmtOptions> { fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
let from = parse_unit(args.value_of(options::FROM).unwrap())?;
let to = parse_unit(args.value_of(options::TO).unwrap())?;
let transform = TransformOptions { let transform = TransformOptions {
from: Transform { from: Transform { unit: from },
unit: args to: Transform { unit: to },
.opt_str("from")
.map(parse_unit)
.unwrap_or(Ok(Unit::None))?,
},
to: Transform {
unit: args
.opt_str("to")
.map(parse_unit)
.unwrap_or(Ok(Unit::None))?,
},
}; };
let padding = match args.opt_str("padding") { let padding = match args.value_of(options::PADDING) {
Some(s) => s.parse::<isize>().map_err(|err| err.to_string()), Some(s) => s.parse::<isize>().map_err(|err| err.to_string()),
None => Ok(0), None => Ok(0),
}?; }?;
let header = match args.opt_default("header", "1") { let header = match args.occurrences_of(options::HEADER) {
Some(s) => s.parse::<usize>().map_err(|err| err.to_string()), 0 => Ok(0),
None => Ok(0), _ => args
.value_of(options::HEADER)
.unwrap()
.parse::<usize>()
.map_err(|err| err.to_string()),
}?; }?;
Ok(NumfmtOptions { Ok(NumfmtOptions {
@ -252,9 +287,9 @@ fn parse_options(args: &Matches) -> Result<NumfmtOptions> {
}) })
} }
fn handle_args(args: &[String], options: NumfmtOptions) -> Result<()> { fn handle_args<'a>(args: impl Iterator<Item = &'a str>, options: NumfmtOptions) -> Result<()> {
for l in args { for l in args {
println!("{}", format_string(l.clone(), &options)?) println!("{}", format_string(l, &options)?)
} }
Ok(()) Ok(())
} }
@ -270,7 +305,7 @@ fn handle_stdin(options: NumfmtOptions) -> Result<()> {
for l in lines { for l in lines {
l.map_err(|e| e.to_string()).and_then(|l| { l.map_err(|e| e.to_string()).and_then(|l| {
let l = format_string(l, &options)?; let l = format_string(l.as_ref(), &options)?;
println!("{}", l); println!("{}", l);
Ok(()) Ok(())
})? })?
@ -279,83 +314,57 @@ fn handle_stdin(options: NumfmtOptions) -> Result<()> {
} }
pub fn uumain(args: impl uucore::Args) -> i32 { pub fn uumain(args: impl uucore::Args) -> i32 {
let args = args.collect_str(); let usage = get_usage();
let mut opts = Options::new(); let matches = App::new(executable!())
.version(VERSION)
opts.optflag("h", "help", "display this help and exit"); .about(ABOUT)
opts.optflag("V", "version", "output version information and exit"); .usage(&usage[..])
opts.optopt( .after_help(LONG_HELP)
"", .arg(
"from", Arg::with_name(options::FROM)
"auto-scale input numbers to UNITs; default is 'none'; see UNIT above", .long(options::FROM)
"UNIT", .help("auto-scale input numbers to UNITs; see UNIT below")
); .value_name("UNIT")
opts.optopt( .default_value(options::FROM_DEFAULT)
"", )
"to", .arg(
"auto-scale output numbers to UNITs; see Unit above", Arg::with_name(options::TO)
"UNIT", .long(options::TO)
); .help("auto-scale output numbers to UNITs; see UNIT below")
opts.optopt( .value_name("UNIT")
"", .default_value(options::TO_DEFAULT)
"padding", )
"pad the output to N characters; positive N will right-align; negative N will left-align; padding is ignored if the output is wider than N", .arg(
"N" Arg::with_name(options::PADDING)
); .long(options::PADDING)
opts.optflagopt( .help("pad the output to N characters; positive N will right-align; negative N will left-align; padding is ignored if the output is wider than N")
"", .value_name("N")
"header", )
"print (without converting) the first N header lines; N defaults to 1 if not specified", .arg(
"N", Arg::with_name(options::HEADER)
); .long(options::HEADER)
.help("print (without converting) the first N header lines; N defaults to 1 if not specified")
let matches = opts.parse(&args[1..]).unwrap(); .value_name("N")
if matches.opt_present("help") { .default_value(options::HEADER_DEFAULT)
println!("{} {}", NAME, VERSION); .hide_default_value(true)
println!(); )
println!("Usage:"); .arg(
println!(" {0} [STRING]... [OPTION]...", NAME); Arg::with_name(options::NUMBER)
println!(); .hidden(true)
print!( .multiple(true)
"{}", )
opts.usage("Convert numbers from/to human-readable strings") .get_matches_from(args);
);
println!(
"UNIT options:
none no auto-scaling is done; suffixes will trigger an error
auto accept optional single/two letter suffix:
1K = 1000, 1Ki = 1024, 1M = 1000000, 1Mi = 1048576,
si accept optional single letter suffix:
1K = 1000, 1M = 1000000, ...
iec accept optional single letter suffix:
1K = 1024, 1M = 1048576, ...
iec-i accept optional two-letter suffix:
1Ki = 1024, 1Mi = 1048576, ..."
);
return 0;
}
if matches.opt_present("version") {
println!("{} {}", NAME, VERSION);
return 0;
}
let options = parse_options(&matches).unwrap(); let options = parse_options(&matches).unwrap();
if matches.free.is_empty() { let result = match matches.values_of(options::NUMBER) {
handle_stdin(options).unwrap() Some(values) => handle_args(values, options),
} else { None => handle_stdin(options),
handle_args(&matches.free, options).unwrap()
}; };
0 match result {
Err(e) => crash!(1, "{}", e),
_ => 0,
}
} }