extern crate regex;
use regex::Regex;
#[macro_export]
macro_rules! n_kb_bytes {
() => {1000u128};
( $x:expr ) => {$x as u128 * 1000u128};
( $x:expr, $t:ty ) => {($x * (1000000 as $t)) as u128 / 1000u128};
}
#[macro_export]
macro_rules! n_kib_bytes {
() => {1024u128};
( $x:expr ) => {$x as u128 * 1024u128};
( $x:expr, $t:ty ) => {($x * (1048576 as $t)) as u128 / 1024u128};
}
#[macro_export]
macro_rules! n_mb_bytes {
() => {1000000u128};
( $x:expr ) => {$x as u128 * 1000000u128};
( $x:expr, $t:ty ) => {($x * (1000000 as $t)) as u128};
}
#[macro_export]
macro_rules! n_mib_bytes {
() => {1048576u128};
( $x:expr ) => {$x as u128 * 1048576u128};
( $x:expr, $t:ty ) => {($x * (1048576 as $t)) as u128};
}
#[macro_export]
macro_rules! n_gb_bytes {
() => {1000000000u128};
( $x:expr ) => {$x as u128 * 1000000000u128};
( $x:expr, $t:ty ) => {($x * (1000000 as $t)) as u128 * 1000u128};
}
#[macro_export]
macro_rules! n_gib_bytes {
() => {1073741824u128};
( $x:expr ) => {$x as u128 * 1073741824u128};
( $x:expr, $t:ty ) => {($x * (1048576 as $t)) as u128 * 1024u128};
}
#[macro_export]
macro_rules! n_tb_bytes {
() => {1000000000000u128};
( $x:expr ) => {$x as u128 * 1000000000000u128};
( $x:expr, $t:ty ) => {($x * (1000000 as $t)) as u128 * 1000000u128};
}
#[macro_export]
macro_rules! n_tib_bytes {
() => {1099511627776u128};
( $x:expr ) => {$x as u128 * 1099511627776u128};
( $x:expr, $t:ty ) => {($x * (1048576 as $t)) as u128 * 1048576u128};
}
#[macro_export]
macro_rules! n_pb_bytes {
() => {1000000000000000u128};
( $x:expr ) => {$x as u128 * 1000000000000000u128};
( $x:expr, $t:ty ) => {($x * (1000000 as $t)) as u128 * 1000000000u128};
}
#[macro_export]
macro_rules! n_pib_bytes {
() => {1125899906842624u128};
( $x:expr ) => {$x as u128 * 1125899906842624u128};
( $x:expr, $t:ty ) => {($x * (1048576 as $t)) as u128 * 1073741824u128};
}
#[derive(Debug, PartialEq, Clone)]
pub enum ByteUnit {
B,
KB,
KiB,
MB,
MiB,
GB,
GiB,
TB,
TiB,
PB,
PiB,
}
impl ToString for ByteUnit {
fn to_string(&self) -> String {
match self {
ByteUnit::B => String::from("B"),
ByteUnit::KB => String::from("KB"),
ByteUnit::KiB => String::from("KiB"),
ByteUnit::MB => String::from("MB"),
ByteUnit::MiB => String::from("MiB"),
ByteUnit::GB => String::from("GB"),
ByteUnit::GiB => String::from("GiB"),
ByteUnit::TB => String::from("TB"),
ByteUnit::TiB => String::from("TiB"),
ByteUnit::PB => String::from("PB"),
ByteUnit::PiB => String::from("PiB"),
}
}
}
#[derive(Debug)]
pub enum ByteError {
ValueIncorrect,
UnitIncorrect,
ParseError,
}
#[derive(Debug)]
struct ByteInner {
bytes: u128
}
impl PartialEq for ByteInner {
fn eq(&self, other: &ByteInner) -> bool {
self.bytes == other.bytes
}
}
#[derive(Debug)]
pub struct Byte(ByteInner);
impl PartialEq for Byte {
fn eq(&self, other: &Byte) -> bool {
self.0 == other.0
}
}
impl ToString for Byte {
fn to_string(&self) -> String {
format!("{}", self.0.bytes)
}
}
impl Byte {
pub fn from_unit(value: f64, unit: ByteUnit) -> Result<Byte, ByteError> {
if value < 0f64 {
return Err(ByteError::ValueIncorrect);
}
let bytes = get_bytes(value, &unit);
Ok(Byte(ByteInner {
bytes
}))
}
pub fn from_bytes(bytes: u128) -> Byte {
Byte(ByteInner {
bytes
})
}
pub fn from_string(string: &str) -> Result<Byte, ByteError> {
let string = string.trim();
let regex = Regex::new(r"^(\d+(\.\d+)?)[\s]*(\S+)?$").unwrap();
let captures = regex.captures(string);
match captures {
Some(captures) => {
let unit = match captures.get(3) {
Some(v) => v.as_str(),
None => "B"
};
let value = match captures[1].parse::<f64>() {
Ok(v) => {
match unit {
"b" => {
if v.floor() != v || v % 8f64 != 0f64 {
return Err(ByteError::ValueIncorrect);
}
v / 8f64
}
_ => v
}
}
Err(_) => return Err(ByteError::ParseError)
};
let unit = match unit.to_uppercase().as_str() {
"B" => ByteUnit::B,
"KB" | "K" => ByteUnit::KB,
"KIB" => ByteUnit::KiB,
"MB" | "M" => ByteUnit::MB,
"MIB" => ByteUnit::MiB,
"GB" | "G" => ByteUnit::GB,
"GIB" => ByteUnit::GiB,
"TB" | "T" => ByteUnit::TB,
"TIB" => ByteUnit::TiB,
"PB" | "P" => ByteUnit::PB,
"PIB" => ByteUnit::PiB,
_ => return Err(ByteError::UnitIncorrect)
};
Byte::from_unit(value, unit)
}
None => Err(ByteError::ParseError)
}
}
pub fn get_bytes(&self) -> u128 {
self.0.bytes
}
pub fn get_appropriate_unit(&self, binary_multiples: bool) -> AdjustedByte {
let bytes = self.0.bytes;
if binary_multiples {
if bytes > n_pib_bytes!() {
AdjustedByte(AdjustedByteInner {
value: bytes as f64 / n_pib_bytes!() as f64,
unit: ByteUnit::PiB,
})
} else if bytes > n_tib_bytes!() {
AdjustedByte(AdjustedByteInner {
value: bytes as f64 / n_tib_bytes!() as f64,
unit: ByteUnit::TiB,
})
} else if bytes > n_gib_bytes!() {
AdjustedByte(AdjustedByteInner {
value: bytes as f64 / n_gib_bytes!() as f64,
unit: ByteUnit::GiB,
})
} else if bytes > n_mib_bytes!() {
AdjustedByte(AdjustedByteInner {
value: bytes as f64 / n_mib_bytes!() as f64,
unit: ByteUnit::MiB,
})
} else if bytes > n_kib_bytes!() {
AdjustedByte(AdjustedByteInner {
value: bytes as f64 / n_kib_bytes!() as f64,
unit: ByteUnit::KiB,
})
} else {
AdjustedByte(AdjustedByteInner {
value: bytes as f64,
unit: ByteUnit::B,
})
}
} else {
if bytes > n_pb_bytes!() {
AdjustedByte(AdjustedByteInner {
value: bytes as f64 / n_pb_bytes!() as f64,
unit: ByteUnit::PB,
})
} else if bytes > n_tb_bytes!() {
AdjustedByte(AdjustedByteInner {
value: bytes as f64 / n_tb_bytes!() as f64,
unit: ByteUnit::TB,
})
} else if bytes > n_gb_bytes!() {
AdjustedByte(AdjustedByteInner {
value: bytes as f64 / n_gb_bytes!() as f64,
unit: ByteUnit::GB,
})
} else if bytes > n_mb_bytes!() {
AdjustedByte(AdjustedByteInner {
value: bytes as f64 / n_mb_bytes!() as f64,
unit: ByteUnit::MB,
})
} else if bytes > n_kb_bytes!() {
AdjustedByte(AdjustedByteInner {
value: bytes as f64 / n_kb_bytes!() as f64,
unit: ByteUnit::KB,
})
} else {
AdjustedByte(AdjustedByteInner {
value: bytes as f64,
unit: ByteUnit::B,
})
}
}
}
pub fn get_adjusted_unit(&self, unit: ByteUnit) -> AdjustedByte {
let bytes = self.0.bytes;
let value = match unit {
ByteUnit::B => bytes as f64,
ByteUnit::KB => bytes as f64 / n_kb_bytes!() as f64,
ByteUnit::KiB => bytes as f64 / n_kib_bytes!() as f64,
ByteUnit::MB => bytes as f64 / n_mb_bytes!() as f64,
ByteUnit::MiB => bytes as f64 / n_mib_bytes!() as f64,
ByteUnit::GB => bytes as f64 / n_gb_bytes!() as f64,
ByteUnit::GiB => bytes as f64 / n_gib_bytes!() as f64,
ByteUnit::TB => bytes as f64 / n_tb_bytes!() as f64,
ByteUnit::TiB => bytes as f64 / n_tib_bytes!() as f64,
ByteUnit::PB => bytes as f64 / n_pb_bytes!() as f64,
ByteUnit::PiB => bytes as f64 / n_pib_bytes!() as f64,
};
AdjustedByte(AdjustedByteInner {
value,
unit,
})
}
}
#[derive(Debug)]
struct AdjustedByteInner {
value: f64,
unit: ByteUnit,
}
impl PartialEq for AdjustedByteInner {
fn eq(&self, other: &AdjustedByteInner) -> bool {
self.value == other.value && self.unit == other.unit
}
}
#[derive(Debug)]
pub struct AdjustedByte(AdjustedByteInner);
impl PartialEq for AdjustedByte {
fn eq(&self, other: &AdjustedByte) -> bool {
let s = &self.0;
let o = &other.0;
if s == o {
return true;
}
let self_value = get_bytes(s.value, &s.unit);
let other_value = get_bytes(o.value, &o.unit);
self_value == other_value
}
}
impl ToString for AdjustedByte {
fn to_string(&self) -> String {
self.format(2)
}
}
impl AdjustedByte {
pub fn format(&self, fractional_digits: usize) -> String {
format!("{:.*} {}", fractional_digits, self.0.value, self.0.unit.to_string())
}
pub fn get_value(&self) -> f64 {
self.0.value
}
pub fn get_unit(&self) -> &ByteUnit {
&self.0.unit
}
}
fn get_bytes(value: f64, unit: &ByteUnit) -> u128 {
match unit {
ByteUnit::B => value as u128,
ByteUnit::KB => n_kb_bytes!(value, f64),
ByteUnit::KiB => n_kib_bytes!(value, f64),
ByteUnit::MB => n_mb_bytes!(value, f64),
ByteUnit::MiB => n_mib_bytes!(value, f64),
ByteUnit::GB => n_gb_bytes!(value, f64),
ByteUnit::GiB => n_gib_bytes!(value, f64),
ByteUnit::TB => n_tb_bytes!(value, f64),
ByteUnit::TiB => n_gib_bytes!(value, f64),
ByteUnit::PB => n_pb_bytes!(value, f64),
ByteUnit::PiB => n_pib_bytes!(value, f64)
}
}
#[cfg(test)]
mod tests {
}