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:
parent
35a7f01d15
commit
9081e120d6
2 changed files with 110 additions and 101 deletions
|
@ -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" }
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue