use std::fmt;
use std::str::FromStr;
use super::{Multiple, ParsingError, SpecificSize};
macro_rules! doc_comment {
($doc:expr, $($tt:tt)*) => {
#[doc = $doc]
$($tt)*
};
}
macro_rules! multiple {
($name:ident, $size:expr, $str:expr) => {
multiple!($name, $size, $str, stringify!($name), stringify!($size));
};
($name:ident, $size:expr, $str:expr, $sname:expr, $ssize:expr) => {
doc_comment! {
concat!("Multiple representing a ", $sname, ".\n\n",
"Represents a size of `value * ", $ssize,
"`. When parsing this multiple from text it expects `",
$str, "`."),
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct $name;
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad($str)
}
}
impl Multiple for $name {
fn from_any(value: f64, multiple: Any) -> SpecificSize<Self> {
let multiply = multiple.multiple_of_bytes() / $size;
let value = value * multiply;
SpecificSize {
value,
multiple: $name,
}
}
fn into_any(size: SpecificSize<Self>) -> (f64, Any) {
(size.value, Any::$name)
}
}
impl From<$name> for Any {
fn from(_multiple: $name) -> Any {
Any::$name
}
}
};
}
multiple!(Byte, 1_f64, "B");
multiple!(Kilobyte, 1000_f64.powi(1), "kB");
multiple!(Megabyte, 1000_f64.powi(2), "MB");
multiple!(Gigabyte, 1000_f64.powi(3), "GB");
multiple!(Terabyte, 1000_f64.powi(4), "TB");
multiple!(Petabyte, 1000_f64.powi(5), "PB");
multiple!(Exabyte, 1000_f64.powi(6), "EB");
multiple!(Zettabyte, 1000_f64.powi(7), "ZB");
multiple!(Yottabyte, 1000_f64.powi(8), "YB");
multiple!(Kibibyte, 1024_f64.powi(1), "KiB");
multiple!(Mebibyte, 1024_f64.powi(2), "MiB");
multiple!(Gigibyte, 1024_f64.powi(3), "GiB");
multiple!(Tebibyte, 1024_f64.powi(4), "TiB");
multiple!(Pebibyte, 1024_f64.powi(5), "PiB");
multiple!(Exbibyte, 1024_f64.powi(6), "EiB");
multiple!(Zebibyte, 1024_f64.powi(7), "ZiB");
multiple!(Yobibyte, 1024_f64.powi(8), "YiB");
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[allow(missing_docs)]
#[non_exhaustive]
pub enum Any {
Byte,
Kilobyte,
Megabyte,
Gigabyte,
Terabyte,
Petabyte,
Exabyte,
Zettabyte,
Yottabyte,
Kibibyte,
Mebibyte,
Gigibyte,
Tebibyte,
Pebibyte,
Exbibyte,
Zebibyte,
Yobibyte,
}
impl Multiple for Any {
fn from_any(value: f64, multiple: Any) -> SpecificSize<Self> {
SpecificSize { value, multiple }
}
fn into_any(size: SpecificSize<Self>) -> (f64, Any) {
(size.value, size.multiple)
}
}
impl Any {
pub(crate) fn multiple_of_bytes(self) -> f64 {
match self {
Any::Byte => 1_f64,
Any::Kilobyte => 1000_f64,
Any::Megabyte => 1000_f64.powi(2),
Any::Gigabyte => 1000_f64.powi(3),
Any::Terabyte => 1000_f64.powi(4),
Any::Petabyte => 1000_f64.powi(5),
Any::Exabyte => 1000_f64.powi(6),
Any::Zettabyte => 1000_f64.powi(7),
Any::Yottabyte => 1000_f64.powi(8),
Any::Kibibyte => 1024_f64,
Any::Mebibyte => 1024_f64.powi(2),
Any::Gigibyte => 1024_f64.powi(3),
Any::Tebibyte => 1024_f64.powi(4),
Any::Pebibyte => 1024_f64.powi(5),
Any::Exbibyte => 1024_f64.powi(6),
Any::Zebibyte => 1024_f64.powi(7),
Any::Yobibyte => 1024_f64.powi(8),
}
}
}
impl FromStr for Any {
type Err = ParsingError;
fn from_str(input: &str) -> Result<Any, Self::Err> {
if input == "KB" {
return Ok(Any::Kibibyte);
}
let units = [
("B", Any::Byte),
("Kb", Any::Kilobyte),
("MB", Any::Megabyte),
("GB", Any::Gigabyte),
("TB", Any::Terabyte),
("PB", Any::Petabyte),
("EB", Any::Exabyte),
("ZB", Any::Zettabyte),
("YB", Any::Yottabyte),
("KIB", Any::Kibibyte),
("MIB", Any::Mebibyte),
("GIB", Any::Gigibyte),
("TIB", Any::Tebibyte),
("PIB", Any::Pebibyte),
("EIB", Any::Exbibyte),
("ZIB", Any::Zebibyte),
("YIB", Any::Yobibyte),
];
for (name, variant) in units {
if input.eq_ignore_ascii_case(name) {
return Ok(variant);
}
}
Err(ParsingError::InvalidMultiple)
}
}
impl fmt::Display for Any {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(match *self {
Any::Byte => "B",
Any::Kilobyte => "kB",
Any::Megabyte => "MB",
Any::Gigabyte => "GB",
Any::Terabyte => "TB",
Any::Petabyte => "PB",
Any::Exabyte => "EB",
Any::Zettabyte => "ZB",
Any::Yottabyte => "YB",
Any::Kibibyte => "KiB",
Any::Mebibyte => "MiB",
Any::Gigibyte => "GiB",
Any::Tebibyte => "TiB",
Any::Pebibyte => "PiB",
Any::Exbibyte => "EiB",
Any::Zebibyte => "ZiB",
Any::Yobibyte => "YiB",
})
}
}