1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
#[cfg(test)]
mod tests;
/// SI unit constants: K, M, G, KB...
pub mod units;
type Result<T> = ::std::result::Result<T, ::std::num::ParseIntError>;
/// Parse a numeric string with an optional one-or-two character multiplicative
/// suffix (K, KB, M, MB, ...)
/// into an integer. Numeric strings may be decimal, octal, or hexadecimal.
/// Multiple numeric strings may be multipled together by separating them with
/// the 'X' character. See `number/units` for a list of the SI suffixes.
///
/// ```
/// #use dd::opts::number::parse;
/// vec![
/// ("base case", "2", Some(2)),
/// ("two ok", "0x18X21", Some(0x18 * 21)),
/// ("mixed types", "0x11X0o22X2", Some(0x11 * 0o22 * 2)),
/// ("multiple suffixes", "0x13MX9K", Some(0x13 * M * 9 * K)),
/// ("bad string", "asjopdajsdomasd", None),
/// ]
/// .into_iter()
/// .for_each(|(name, s, want)| {
/// let got = parse(s).ok();
/// assert_eq!(
/// want, got,
/// r#"{}: want number("{}") = {:?}, but got {:?}"#,
/// name, s, want, got
/// );
/// })
/// ```
pub fn parse(s: &str) -> Result<usize> {
let res: Result<Vec<usize>> = s.split('X').map(|s| scaled_part(s.trim())).collect();
Ok(res?.into_iter().product())
}
/// Wrapper around [`parse`] that wraps the usize in an [Option]
pub fn parse_opt(s: &str) -> Result<Option<usize>> { Ok(Some(parse(s)?)) }
/// parse a number in the form [NUM][SCALE] to a usize,
/// where NUM is a hexidecimal, octal, or decimal number,
/// and scale is a one-or two byte suffix. see the documentation of parse for
/// more.
fn scaled_part(s: &str) -> Result<usize> {
let (scale_unit, offset) = scaling_factor(s);
let end = s.len() - offset;
Ok(scale_unit * numeric_part(&s[..end])?)
}
/// parse a string to a number, using the starting two characters to determine
/// whether it is hexidecimal, octal, or decimal
fn numeric_part(s: &str) -> Result<usize> {
let mut bytes = s.bytes();
match (bytes.next(), bytes.next()) {
// hexadecimal
(Some(b'0'), Some(b'x')) | (Some(b'0'), Some(b'X')) => usize::from_str_radix(&s[2..], 16),
// octal
(Some(b'0'), Some(b'o')) => usize::from_str_radix(&s[2..], 8),
// assume deciaml
_ => usize::from_str_radix(s, 10),
}
}
/// parse the suffix of the string to find a scaling factor, if any
/// and return the length of the suffix
fn scaling_factor(s: &str) -> (usize, usize) {
let (scaling, offset) = if let Some(x) = two_byte_scaling_factor(s) {
(x, 2)
} else if let Some(x) = one_byte_scaling_factor(s) {
(x, 1)
} else {
(1, 0)
};
(scaling, offset)
}
fn two_byte_scaling_factor(s: &str) -> Option<usize> {
use self::units::*;
let mut bytes = s.bytes();
if Some(b'B') != bytes.next_back() {
return None;
}
match bytes.next_back()? {
b'K' => Some(KB),
b'M' => Some(MB),
b'G' => Some(GB),
b'T' => Some(TB),
b'P' => Some(PB),
b'Z' => Some(ZB),
_ => None,
}
}
fn one_byte_scaling_factor(s: &str) -> Option<usize> {
use self::units::*;
Some(match s.bytes().next_back()? {
b'C' => 1,
b'W' => BITSIZE,
b'B' => B,
b'K' => K,
b'M' => M,
b'G' => G,
b'T' => T,
b'P' => P,
b'Z' => Z,
// (_, b'y') => Y, // would have to use u128
_ => return None,
})
}