extern crate regex;
#[macro_use]
extern crate lazy_static;
mod macros;
mod byte_unit;
pub use byte_unit::ByteUnit;
use std::fmt::{self, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::mem::transmute;
use regex::Regex;
lazy_static! {
static ref BYTE_RE: Regex = {
Regex::new(r"^(\d+(\.\d+)?)[\s]*(?i)((([ptgmk])(i)?)?b?)$").unwrap()
};
}
#[derive(Debug, PartialEq, Eq)]
pub enum ByteError {
ValueIncorrect,
UnitIncorrect,
ParseError,
}
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct Byte {
bytes: u128
}
impl Display for Byte {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
f.write_fmt(format_args!("{}", self.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 {
bytes
})
}
pub fn from_bytes(bytes: u128) -> Byte {
Byte {
bytes
}
}
pub fn from_string<S: AsRef<str>>(string: S) -> Result<Byte, ByteError> {
let string = string.as_ref();
let string = string.trim();
let captures = BYTE_RE.captures(string);
match captures {
Some(captures) => {
let unit = match captures.get(3) {
Some(_) => {
match captures.get(5) {
Some(m) => {
let u: String = m.as_str().to_lowercase();
match captures.get(6) {
Some(_) => {
match u.as_str() {
"k" => ByteUnit::KiB,
"m" => ByteUnit::MiB,
"g" => ByteUnit::GiB,
"t" => ByteUnit::TiB,
"p" => ByteUnit::PiB,
_ => unreachable!()
}
}
None => {
match u.as_str() {
"k" => ByteUnit::KB,
"m" => ByteUnit::MB,
"g" => ByteUnit::GB,
"t" => ByteUnit::TB,
"p" => ByteUnit::PB,
_ => unreachable!()
}
}
}
}
None => {
ByteUnit::B
}
}
}
None => ByteUnit::B
};
let value = match captures[1].parse::<f64>() {
Ok(v) => v,
Err(_) => return Err(ByteError::ParseError)
};
Byte::from_unit(value, unit)
}
None => Err(ByteError::ParseError)
}
}
pub fn get_bytes(&self) -> u128 {
self.bytes
}
pub fn get_appropriate_unit(&self, binary_multiples: bool) -> AdjustedByte {
let bytes = self.bytes;
if binary_multiples {
if bytes > n_pib_bytes!() {
AdjustedByte {
value: bytes as f64 / n_pib_bytes!() as f64,
unit: ByteUnit::PiB,
}
} else if bytes > n_tib_bytes!() {
AdjustedByte {
value: bytes as f64 / n_tib_bytes!() as f64,
unit: ByteUnit::TiB,
}
} else if bytes > n_gib_bytes!() {
AdjustedByte {
value: bytes as f64 / n_gib_bytes!() as f64,
unit: ByteUnit::GiB,
}
} else if bytes > n_mib_bytes!() {
AdjustedByte {
value: bytes as f64 / n_mib_bytes!() as f64,
unit: ByteUnit::MiB,
}
} else if bytes > n_kib_bytes!() {
AdjustedByte {
value: bytes as f64 / n_kib_bytes!() as f64,
unit: ByteUnit::KiB,
}
} else {
AdjustedByte {
value: bytes as f64,
unit: ByteUnit::B,
}
}
} else {
if bytes > n_pb_bytes!() {
AdjustedByte {
value: bytes as f64 / n_pb_bytes!() as f64,
unit: ByteUnit::PB,
}
} else if bytes > n_tb_bytes!() {
AdjustedByte {
value: bytes as f64 / n_tb_bytes!() as f64,
unit: ByteUnit::TB,
}
} else if bytes > n_gb_bytes!() {
AdjustedByte {
value: bytes as f64 / n_gb_bytes!() as f64,
unit: ByteUnit::GB,
}
} else if bytes > n_mb_bytes!() {
AdjustedByte {
value: bytes as f64 / n_mb_bytes!() as f64,
unit: ByteUnit::MB,
}
} else if bytes > n_kb_bytes!() {
AdjustedByte {
value: bytes as f64 / n_kb_bytes!() as f64,
unit: ByteUnit::KB,
}
} else {
AdjustedByte {
value: bytes as f64,
unit: ByteUnit::B,
}
}
}
}
pub fn get_adjusted_unit(&self, unit: ByteUnit) -> AdjustedByte {
let bytes = self.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 {
value,
unit,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct AdjustedByte {
value: f64,
unit: ByteUnit,
}
impl PartialEq for AdjustedByte {
fn eq(&self, other: &AdjustedByte) -> bool {
if self.value == other.value && self.unit == other.unit {
return true;
}
let self_value = get_bytes(self.value, self.unit);
let other_value = get_bytes(other.value, other.unit);
self_value == other_value
}
}
impl AdjustedByte {
pub fn format(&self, fractional_digits: usize) -> String {
format!("{:.*} {}", fractional_digits, self.value, self.unit.to_string())
}
pub fn get_value(&self) -> f64 {
self.value
}
pub fn get_unit(&self) -> ByteUnit {
self.unit
}
}
impl Display for AdjustedByte {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
f.write_fmt(format_args!("{:.2} ", self.value))?;
Display::fmt(&self.unit, f)
}
}
impl Eq for AdjustedByte {}
impl Hash for AdjustedByte {
fn hash<H: Hasher>(&self, state: &mut H) {
let bytes: [u8; 8] = unsafe { transmute(self.value) };
state.write(&bytes);
self.unit.hash(state);
}
}
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)
}
}