mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 19:47:45 +00:00
numfmt: add suffixes and fix negative numbers
Also refactors the code.
This commit is contained in:
parent
07b01a85f9
commit
1af0484360
2 changed files with 130 additions and 107 deletions
|
@ -12,12 +12,26 @@
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
|
|
||||||
use getopts::{Matches, Options};
|
use getopts::{Matches, Options};
|
||||||
use std::io::BufRead;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::io::BufRead;
|
||||||
|
|
||||||
static NAME: &'static str = "numfmt";
|
static NAME: &'static str = "numfmt";
|
||||||
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
const IEC_BASES: [f64; 10] = [
|
||||||
|
//premature optimization
|
||||||
|
1.,
|
||||||
|
1024.,
|
||||||
|
1048576.,
|
||||||
|
1073741824.,
|
||||||
|
1099511627776.,
|
||||||
|
1125899906842624.,
|
||||||
|
1152921504606846976.,
|
||||||
|
1180591620717411303424.,
|
||||||
|
1208925819614629174706176.,
|
||||||
|
1237940039285380274899124224.,
|
||||||
|
];
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, String>;
|
type Result<T> = std::result::Result<T, String>;
|
||||||
|
|
||||||
enum TransformDirection {
|
enum TransformDirection {
|
||||||
|
@ -25,68 +39,73 @@ enum TransformDirection {
|
||||||
To,
|
To,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WithI = bool;
|
||||||
|
|
||||||
enum Unit {
|
enum Unit {
|
||||||
Auto,
|
Auto,
|
||||||
Si,
|
Si,
|
||||||
Iec,
|
Iec(WithI),
|
||||||
IecI,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Suffix {
|
enum RawSuffix {
|
||||||
K,
|
K,
|
||||||
M,
|
M,
|
||||||
G,
|
G,
|
||||||
T,
|
T,
|
||||||
P,
|
P,
|
||||||
E,
|
E,
|
||||||
Ki,
|
Z,
|
||||||
Mi,
|
Y,
|
||||||
Gi,
|
|
||||||
Ti,
|
|
||||||
Pi,
|
|
||||||
Ei,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Suffix {
|
type Suffix = (RawSuffix, WithI);
|
||||||
|
|
||||||
|
struct DisplayableSuffix(Suffix);
|
||||||
|
|
||||||
|
impl fmt::Display for DisplayableSuffix {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
let DisplayableSuffix((ref raw_suffix, ref with_i)) = *self;
|
||||||
Suffix::K => write!(f, "K"),
|
match raw_suffix {
|
||||||
Suffix::M => write!(f, "M"),
|
RawSuffix::K => write!(f, "K"),
|
||||||
Suffix::G => write!(f, "G"),
|
RawSuffix::M => write!(f, "M"),
|
||||||
Suffix::T => write!(f, "T"),
|
RawSuffix::G => write!(f, "G"),
|
||||||
Suffix::P => write!(f, "P"),
|
RawSuffix::T => write!(f, "T"),
|
||||||
Suffix::E => write!(f, "E"),
|
RawSuffix::P => write!(f, "P"),
|
||||||
Suffix::Ki => write!(f, "Ki"),
|
RawSuffix::E => write!(f, "E"),
|
||||||
Suffix::Mi => write!(f, "Mi"),
|
RawSuffix::Z => write!(f, "Z"),
|
||||||
Suffix::Gi => write!(f, "Gi"),
|
RawSuffix::Y => write!(f, "Y"),
|
||||||
Suffix::Ti => write!(f, "Ti"),
|
}.and_then(|()| match with_i {
|
||||||
Suffix::Pi => write!(f, "Pi"),
|
true => write!(f, "i"),
|
||||||
Suffix::Ei => write!(f, "Ei"),
|
false => Ok(()),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_suffix(s: String) -> Result<(f64, Option<Suffix>)> {
|
fn parse_suffix(s: String) -> Result<(f64, Option<Suffix>)> {
|
||||||
|
let with_i = s.ends_with("i");
|
||||||
let mut iter = s.chars();
|
let mut iter = s.chars();
|
||||||
let (suffix, suffix_len) = match iter.next_back() {
|
if with_i {
|
||||||
Some('K') => Ok((Some(Suffix::K), 1)),
|
iter.next_back();
|
||||||
Some('M') => Ok((Some(Suffix::M), 1)),
|
}
|
||||||
Some('G') => Ok((Some(Suffix::G), 1)),
|
let suffix: Option<Suffix> = match iter.next_back() {
|
||||||
Some('T') => Ok((Some(Suffix::T), 1)),
|
Some('K') => Ok(Some((RawSuffix::K, with_i))),
|
||||||
Some('P') => Ok((Some(Suffix::P), 1)),
|
Some('M') => Ok(Some((RawSuffix::M, with_i))),
|
||||||
Some('E') => Ok((Some(Suffix::E), 1)),
|
Some('G') => Ok(Some((RawSuffix::G, with_i))),
|
||||||
Some('i') => match iter.next_back() {
|
Some('T') => Ok(Some((RawSuffix::T, with_i))),
|
||||||
Some('K') => Ok((Some(Suffix::Ki), 2)),
|
Some('P') => Ok(Some((RawSuffix::P, with_i))),
|
||||||
Some('M') => Ok((Some(Suffix::Mi), 2)),
|
Some('E') => Ok(Some((RawSuffix::E, with_i))),
|
||||||
Some('G') => Ok((Some(Suffix::Gi), 2)),
|
Some('Z') => Ok(Some((RawSuffix::Z, with_i))),
|
||||||
Some('T') => Ok((Some(Suffix::Ti), 2)),
|
Some('Y') => Ok(Some((RawSuffix::Y, with_i))),
|
||||||
Some('P') => Ok((Some(Suffix::Pi), 2)),
|
Some('0'...'9') => Ok(None),
|
||||||
Some('E') => Ok((Some(Suffix::Ei), 2)),
|
_ => Err("Failed to parse suffix"),
|
||||||
_ => Err("Failed to parse suffix"),
|
|
||||||
},
|
|
||||||
_ => Ok((None, 0)),
|
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
let suffix_len = match suffix {
|
||||||
|
None => 0,
|
||||||
|
Some((_, false)) => 1,
|
||||||
|
Some((_, true)) => 2,
|
||||||
|
};
|
||||||
|
|
||||||
let number = s[..s.len() - suffix_len]
|
let number = s[..s.len() - suffix_len]
|
||||||
.parse::<f64>()
|
.parse::<f64>()
|
||||||
.map_err(|err| err.to_string())?;
|
.map_err(|err| err.to_string())?;
|
||||||
|
@ -98,8 +117,8 @@ fn parse_unit(s: String) -> 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),
|
"iec" => Ok(Unit::Iec(false)),
|
||||||
"iec-i" => Ok(Unit::IecI),
|
"iec-i" => Ok(Unit::Iec(true)),
|
||||||
_ => Err("Unsupported unit is specified".to_owned()),
|
_ => Err("Unsupported unit is specified".to_owned()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,36 +137,30 @@ struct NumfmtOptions {
|
||||||
fn remove_suffix(i: f64, s: Option<Suffix>, u: &Unit) -> Result<f64> {
|
fn remove_suffix(i: f64, s: Option<Suffix>, u: &Unit) -> Result<f64> {
|
||||||
match (s, u) {
|
match (s, u) {
|
||||||
(None, _) => Ok(i),
|
(None, _) => Ok(i),
|
||||||
(Some(Suffix::K), &Unit::Auto) | (Some(Suffix::K), &Unit::Si) => Ok(i * 1000.),
|
(Some((raw_suffix, false)), &Unit::Auto) | (Some((raw_suffix, false)), &Unit::Si) => {
|
||||||
(Some(Suffix::M), &Unit::Auto) | (Some(Suffix::M), &Unit::Si) => Ok(i * 1000_000.),
|
match raw_suffix {
|
||||||
(Some(Suffix::G), &Unit::Auto) | (Some(Suffix::G), &Unit::Si) => Ok(i * 1000_000_000.),
|
RawSuffix::K => Ok(i * 1e3),
|
||||||
(Some(Suffix::T), &Unit::Auto) | (Some(Suffix::T), &Unit::Si) => Ok(i * 1000_000_000_000.),
|
RawSuffix::M => Ok(i * 1e6),
|
||||||
(Some(Suffix::P), &Unit::Auto) | (Some(Suffix::P), &Unit::Si) => {
|
RawSuffix::G => Ok(i * 1e9),
|
||||||
Ok(i * 1000_000_000_000_000.)
|
RawSuffix::T => Ok(i * 1e12),
|
||||||
|
RawSuffix::P => Ok(i * 1e15),
|
||||||
|
RawSuffix::E => Ok(i * 1e18),
|
||||||
|
RawSuffix::Z => Ok(i * 1e21),
|
||||||
|
RawSuffix::Y => Ok(i * 1e24),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(Some(Suffix::E), &Unit::Auto) | (Some(Suffix::E), &Unit::Si) => {
|
(Some((raw_suffix, false)), &Unit::Iec(false))
|
||||||
Ok(i * 1000_000_000_000_000_000.)
|
| (Some((raw_suffix, true)), &Unit::Auto)
|
||||||
}
|
| (Some((raw_suffix, true)), &Unit::Iec(true)) => match raw_suffix {
|
||||||
|
RawSuffix::K => Ok(i * IEC_BASES[1]),
|
||||||
(Some(Suffix::Ki), &Unit::Auto)
|
RawSuffix::M => Ok(i * IEC_BASES[2]),
|
||||||
| (Some(Suffix::Ki), &Unit::IecI)
|
RawSuffix::G => Ok(i * IEC_BASES[3]),
|
||||||
| (Some(Suffix::K), &Unit::Iec) => Ok(i * 1024.),
|
RawSuffix::T => Ok(i * IEC_BASES[4]),
|
||||||
(Some(Suffix::Mi), &Unit::Auto)
|
RawSuffix::P => Ok(i * IEC_BASES[5]),
|
||||||
| (Some(Suffix::Mi), &Unit::IecI)
|
RawSuffix::E => Ok(i * IEC_BASES[6]),
|
||||||
| (Some(Suffix::M), &Unit::Iec) => Ok(i * 1048576.),
|
RawSuffix::Z => Ok(i * IEC_BASES[7]),
|
||||||
(Some(Suffix::Gi), &Unit::Auto)
|
RawSuffix::Y => Ok(i * IEC_BASES[8]),
|
||||||
| (Some(Suffix::Gi), &Unit::IecI)
|
},
|
||||||
| (Some(Suffix::G), &Unit::Iec) => Ok(i * 1073741824.),
|
|
||||||
(Some(Suffix::Ti), &Unit::Auto)
|
|
||||||
| (Some(Suffix::Ti), &Unit::IecI)
|
|
||||||
| (Some(Suffix::T), &Unit::Iec) => Ok(i * 1099511627776.),
|
|
||||||
(Some(Suffix::Pi), &Unit::Auto)
|
|
||||||
| (Some(Suffix::Pi), &Unit::IecI)
|
|
||||||
| (Some(Suffix::P), &Unit::Iec) => Ok(i * 1125899906842624.),
|
|
||||||
(Some(Suffix::Ei), &Unit::Auto)
|
|
||||||
| (Some(Suffix::Ei), &Unit::IecI)
|
|
||||||
| (Some(Suffix::E), &Unit::Iec) => Ok(i * 1152921504606846976.),
|
|
||||||
|
|
||||||
(_, _) => Err("This suffix is unsupported for specified unit".to_owned()),
|
(_, _) => Err("This suffix is unsupported for specified unit".to_owned()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,37 +171,30 @@ fn transform_from(s: String, unit: &Unit) -> Result<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consider_suffix(i: f64, u: &Unit) -> Result<(f64, Option<Suffix>)> {
|
fn consider_suffix(i: f64, u: &Unit) -> Result<(f64, Option<Suffix>)> {
|
||||||
|
let j = i.abs();
|
||||||
match *u {
|
match *u {
|
||||||
Unit::Si => match i {
|
Unit::Si => match j {
|
||||||
_ if i < 1000. => Ok((i, None)),
|
_ if j < 1e3 => Ok((i, None)),
|
||||||
_ if i < 1000_000. => Ok((i / 1000., Some(Suffix::K))),
|
_ if j < 1e6 => Ok((i / 1e3, Some((RawSuffix::K, false)))),
|
||||||
_ if i < 1000_000_000. => Ok((i / 1000_000., Some(Suffix::M))),
|
_ if j < 1e9 => Ok((i / 1e6, Some((RawSuffix::M, false)))),
|
||||||
_ if i < 1000_000_000_000. => Ok((i / 1000_000_000., Some(Suffix::G))),
|
_ if j < 1e12 => Ok((i / 1e9, Some((RawSuffix::G, false)))),
|
||||||
_ if i < 1000_000_000_000_000. => Ok((i / 1000_000_000_000., Some(Suffix::T))),
|
_ if j < 1e15 => Ok((i / 1e12, Some((RawSuffix::T, false)))),
|
||||||
_ if i < 1000_000_000_000_000_000. => Ok((i / 1000_000_000_000_000., Some(Suffix::P))),
|
_ if j < 1e18 => Ok((i / 1e15, Some((RawSuffix::P, false)))),
|
||||||
_ if i < 1000_000_000_000_000_000_000. => {
|
_ if j < 1e21 => Ok((i / 1e18, Some((RawSuffix::E, false)))),
|
||||||
Ok((i / 1000_000_000_000_000_000., Some(Suffix::E)))
|
_ if j < 1e24 => Ok((i / 1e21, Some((RawSuffix::Z, false)))),
|
||||||
}
|
_ if j < 1e27 => Ok((i / 1e24, Some((RawSuffix::Y, false)))),
|
||||||
_ => Err("Number is too big and unsupported".to_owned()),
|
_ => Err("Number is too big and unsupported".to_owned()),
|
||||||
},
|
},
|
||||||
Unit::Iec => match i {
|
Unit::Iec(with_i) => match j {
|
||||||
_ if i < 1024. => Ok((i, None)),
|
_ if j < IEC_BASES[1] => Ok((i, None)),
|
||||||
_ if i < 1048576. => Ok((i / 1024., Some(Suffix::K))),
|
_ if j < IEC_BASES[2] => Ok((i / IEC_BASES[1], Some((RawSuffix::K, with_i)))),
|
||||||
_ if i < 1073741824. => Ok((i / 1048576., Some(Suffix::M))),
|
_ if j < IEC_BASES[3] => Ok((i / IEC_BASES[2], Some((RawSuffix::M, with_i)))),
|
||||||
_ if i < 1099511627776. => Ok((i / 1073741824., Some(Suffix::G))),
|
_ if j < IEC_BASES[4] => Ok((i / IEC_BASES[3], Some((RawSuffix::G, with_i)))),
|
||||||
_ if i < 1125899906842624. => Ok((i / 1099511627776., Some(Suffix::T))),
|
_ if j < IEC_BASES[5] => Ok((i / IEC_BASES[4], Some((RawSuffix::T, with_i)))),
|
||||||
_ if i < 1152921504606846976. => Ok((i / 1125899906842624., Some(Suffix::P))),
|
_ if j < IEC_BASES[6] => Ok((i / IEC_BASES[5], Some((RawSuffix::P, with_i)))),
|
||||||
_ if i < 1180591620717411303424. => Ok((i / 1152921504606846976., Some(Suffix::E))),
|
_ if j < IEC_BASES[7] => Ok((i / IEC_BASES[6], Some((RawSuffix::E, with_i)))),
|
||||||
_ => Err("Number is too big and unsupported".to_owned()),
|
_ if j < IEC_BASES[8] => Ok((i / IEC_BASES[7], Some((RawSuffix::Z, with_i)))),
|
||||||
},
|
_ if j < IEC_BASES[9] => Ok((i / IEC_BASES[8], Some((RawSuffix::Y, with_i)))),
|
||||||
Unit::IecI => match i {
|
|
||||||
_ if i < 1024. => Ok((i, None)),
|
|
||||||
_ if i < 1048576. => Ok((i / 1024., Some(Suffix::Ki))),
|
|
||||||
_ if i < 1073741824. => Ok((i / 1048576., Some(Suffix::Mi))),
|
|
||||||
_ if i < 1099511627776. => Ok((i / 1073741824., Some(Suffix::Gi))),
|
|
||||||
_ if i < 1125899906842624. => Ok((i / 1099511627776., Some(Suffix::Ti))),
|
|
||||||
_ if i < 1152921504606846976. => Ok((i / 1125899906842624., Some(Suffix::Pi))),
|
|
||||||
_ if i < 1180591620717411303424. => Ok((i / 1152921504606846976., Some(Suffix::Ei))),
|
|
||||||
_ => Err("Number is too big and unsupported".to_owned()),
|
_ => Err("Number is too big and unsupported".to_owned()),
|
||||||
},
|
},
|
||||||
Unit::Auto => Err("Unit 'auto' isn't supported with --to options".to_owned()),
|
Unit::Auto => Err("Unit 'auto' isn't supported with --to options".to_owned()),
|
||||||
|
@ -200,7 +206,7 @@ fn transform_to(s: String, unit: &Unit) -> Result<String> {
|
||||||
let (i2, s) = consider_suffix(i, unit)?;
|
let (i2, s) = consider_suffix(i, unit)?;
|
||||||
Ok(match s {
|
Ok(match s {
|
||||||
None => format!("{}", i2),
|
None => format!("{}", i2),
|
||||||
Some(s) => format!("{:.1}{}", i2, s),
|
Some(s) => format!("{:.1}{}", i2, DisplayableSuffix(s)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +227,10 @@ fn parse_options(args: &Matches) -> Result<NumfmtOptions> {
|
||||||
let transform = if args.opt_present("from") {
|
let transform = if args.opt_present("from") {
|
||||||
TransformOptions {
|
TransformOptions {
|
||||||
direction: TransformDirection::From,
|
direction: TransformDirection::From,
|
||||||
unit: parse_unit(args.opt_str("from").ok_or("'--from' should have argument")?)?,
|
unit: parse_unit(
|
||||||
|
args.opt_str("from")
|
||||||
|
.ok_or("'--from' should have argument")?,
|
||||||
|
)?,
|
||||||
}
|
}
|
||||||
} else if args.opt_present("to") {
|
} else if args.opt_present("to") {
|
||||||
TransformOptions {
|
TransformOptions {
|
||||||
|
@ -320,19 +329,19 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
none no auto-scaling is done; suffixes will trigger an error
|
none no auto-scaling is done; suffixes will trigger an error
|
||||||
|
|
||||||
auto accept optional single/two letter suffix:
|
auto accept optional single/two letter suffix:
|
||||||
|
|
||||||
1K = 1000, 1Ki = 1024, 1M = 1000000, 1Mi = 1048576,
|
1K = 1000, 1Ki = 1024, 1M = 1000000, 1Mi = 1048576,
|
||||||
|
|
||||||
si accept optional single letter suffix:
|
si accept optional single letter suffix:
|
||||||
|
|
||||||
1K = 1000, 1M = 1000000, ...
|
1K = 1000, 1M = 1000000, ...
|
||||||
|
|
||||||
iec accept optional single letter suffix:
|
iec accept optional single letter suffix:
|
||||||
|
|
||||||
1K = 1024, 1M = 1048576, ...
|
1K = 1024, 1M = 1048576, ...
|
||||||
|
|
||||||
iec-i accept optional two-letter suffix:
|
iec-i accept optional two-letter suffix:
|
||||||
|
|
||||||
1Ki = 1024, 1Mi = 1048576, ..."
|
1Ki = 1024, 1Mi = 1048576, ..."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -106,3 +106,17 @@ fn test_header_default() {
|
||||||
.run()
|
.run()
|
||||||
.stdout_is("header\n1000\n1100000\n100000000");
|
.stdout_is("header\n1000\n1100000\n100000000");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_negative() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--from=si"])
|
||||||
|
.pipe_in("-1000\n-1.1M\n-0.1G")
|
||||||
|
.run()
|
||||||
|
.stdout_is("-1000\n-1100000\n-100000000");
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["--to=iec-i"])
|
||||||
|
.pipe_in("-1024\n-1153434\n-107374182")
|
||||||
|
.run()
|
||||||
|
.stdout_is("-1.0Ki\n-1.1Mi\n-102.4Mi");
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue