dd_lib/opts/
number.rs

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