dd-lib 0.2.1

library functions for a clone of the unix coreutil dd
Documentation
use 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 [`units`] for a list of the SI suffixes.
///
/// ```
/// # use dd_lib::opts::parse;
/// # use dd_lib::units;
/// let tt = vec![
///     ("base case", "2", Some(2)),
///     ("two ok", "0x18X21", Some(0x18 * 21)),
///     ("mixed types", "0x11X0o22X2", Some(0x11 * 0o22 * 2)),
///     ("multiple suffixes", "0x13MX9K", Some(0x13 * units::M * 9 * units::K)),
///     ("bad string", "asjopdajsdomasd", None),
/// ];
/// for (name, s, want) in tt {
///     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)?)) }

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])?)
}

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> {
    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> {
    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,
    })
}
///

pub mod units {}