use anyhow::{anyhow, Result};
use lazy_static::lazy_static;
use regex::Regex;
lazy_static! {
static ref SIZE_CAPTURES: Regex = Regex::new(r"(?i)^(\d+)(b|[kmgt]i?b?)$").unwrap();
}
const KILO: u64 = 1000;
const MEGA: u64 = KILO * 1000;
const GIGA: u64 = MEGA * 1000;
const TERA: u64 = GIGA * 1000;
const KIBI: u64 = 1024;
const MEBI: u64 = KIBI * 1024;
const GIBI: u64 = MEBI * 1024;
const TEBI: u64 = GIBI * 1024;
pub trait FromByteUnit: Sized {
type Err;
fn from_str_byte_unit(str: impl Into<String>) -> Result<Self, Self::Err>;
}
impl FromByteUnit for u64 {
type Err = anyhow::Error;
fn from_str_byte_unit(str: impl Into<String>) -> Result<Self, Self::Err> {
let str = str.into();
let captures = SIZE_CAPTURES
.captures(str.as_str())
.ok_or_else(|| anyhow!("Invalid size representation"))?;
let quantity = captures
.get(1)
.and_then(|v| v.as_str().parse::<u64>().ok())
.ok_or_else(|| anyhow!("Not a number"))?;
let multiplier = match &captures.get(2).map_or("b", |m| m.as_str()).to_lowercase()[..] {
v if v.starts_with("ki") => KIBI,
v if v.starts_with('k') => KILO,
v if v.starts_with("mi") => MEBI,
v if v.starts_with('m') => MEGA,
v if v.starts_with("gi") => GIBI,
v if v.starts_with('g') => GIGA,
v if v.starts_with("ti") => TEBI,
v if v.starts_with('t') => TERA,
"b" => 1,
_ => return Err(anyhow!("This doesn't work!")),
};
let size = quantity * multiplier;
Ok(size)
}
}