use units::*;
type Result<T> = std::result::Result<T, ::std::num::ParseIntError>;
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())
}
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()) {
(Some(b'0'), Some(b'x')) | (Some(b'0'), Some(b'X')) => usize::from_str_radix(&s[2..], 16),
(Some(b'0'), Some(b'o')) => usize::from_str_radix(&s[2..], 8),
_ => usize::from_str_radix(s, 10),
}
}
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,
_ => return None,
})
}
pub mod units {}