use super::*;
use core::fmt;
#[non_exhaustive]
#[derive(Copy, Clone, Debug)]
pub enum Base {
Base2,
Base10,
}
enum Unit {
Byte,
Kibibyte,
Kilobyte,
Mebibyte,
Megabyte,
Gibibyte,
Gigabyte,
Tebibyte,
Terabyte,
Pebibyte,
Petabyte,
Exbibyte,
Exabyte,
}
impl Unit {
#[rustfmt::skip]
const fn text(&self) -> (&'static str, &'static str, &'static str, &'static str) {
use self::Unit::*;
match self {
Byte => ("byte", "Byte", "b", "B"),
Kilobyte => ("kilobyte", "Kilobyte", "kb", "KB"),
Megabyte => ("megabyte", "Megabyte", "mb", "MB"),
Gigabyte => ("gigabyte", "Gigabyte", "gb", "GB"),
Terabyte => ("terabyte", "Terabyte", "tb", "TB"),
Petabyte => ("petabyte", "Petabyte", "pb", "PB"),
Exabyte => ("exabyte", "Exabyte", "eb", "EB"),
Kibibyte => ("kibibyte", "Kibibyte", "kib", "KiB"),
Mebibyte => ("mebibyte", "Mebibyte", "mib", "MiB"),
Gibibyte => ("gibibyte", "Gibibyte", "gib", "GiB"),
Pebibyte => ("pebibyte", "Pebibyte", "pib", "PiB"),
Tebibyte => ("tebibyte", "Tebibyte", "tib", "TiB"),
Exbibyte => ("exbibyte", "Exbibyte", "eib", "EiB"),
}
}
fn format(&self, fmt: &mut fmt::Formatter, bytes: u64, style: &Style) -> fmt::Result {
match (&style, bytes) {
(&Style::Default, _) => match &self {
&Unit::Byte => self.format(fmt, bytes, &Style::FullLowercase),
_ => self.format(fmt, bytes, &Style::Abbreviated),
},
(&Style::FullLowercase, 1) => write!(fmt, " {}", self.text().0),
(&Style::Full, 1) => write!(fmt, " {}", self.text().1),
(&Style::AbbreviatedLowercase, 1) => write!(fmt, " {}", self.text().2),
(&Style::Abbreviated, 1) => write!(fmt, " {}", self.text().3),
(&Style::FullLowercase, _) => write!(fmt, " {}s", self.text().0),
(&Style::Full, _) => write!(fmt, " {}s", self.text().1),
(&Style::AbbreviatedLowercase, _) => write!(fmt, " {}", self.text().2),
(&Style::Abbreviated, _) => write!(fmt, " {}", self.text().3),
}
}
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug)]
pub enum Style {
Default,
Abbreviated,
AbbreviatedLowercase,
Full,
FullLowercase,
}
impl Style {
#[doc(hidden)]
#[allow(non_upper_case_globals)]
#[deprecated(since = "0.3.0", note = "Use Style::Default instead")]
pub const Smart: Style = Style::Default;
#[doc(hidden)]
#[allow(non_upper_case_globals)]
#[deprecated(since = "0.3.0", note = "Use Style::AbbreviatedLowercase instead")]
pub const AbbreviatedLowerCase: Style = Style::AbbreviatedLowercase;
#[doc(hidden)]
#[allow(non_upper_case_globals)]
#[deprecated(since = "0.3.0", note = "Use Style::FullLowercase instead")]
pub const FullLowerCase: Style = Style::FullLowercase;
}
impl std::fmt::Display for Size {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", self.format())
}
}
mod sealed {
pub trait FormatterSize {}
impl FormatterSize for () {}
impl<'a> FormatterSize for &'a crate::Size {}
}
pub struct SizeFormatter<T: sealed::FormatterSize = ()> {
size: T,
base: Base,
style: Style,
}
impl Default for SizeFormatter<()> {
fn default() -> Self {
Self::new()
}
}
struct FmtRenderer<F: Fn(&mut fmt::Formatter) -> fmt::Result> {
formatter: F,
}
impl<F: Fn(&mut fmt::Formatter) -> fmt::Result> FmtRenderer<F> {
pub fn new(formatter: F) -> Self {
Self { formatter }
}
}
impl<F: Fn(&mut fmt::Formatter) -> fmt::Result> fmt::Display for FmtRenderer<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.formatter)(f)
}
}
impl<T: sealed::FormatterSize> SizeFormatter<T> {
pub fn with_base(self, base: Base) -> Self {
Self { base, ..self }
}
pub fn with_style(self, style: Style) -> Self {
Self { style, ..self }
}
fn inner_fmt(&self, fmt: &mut fmt::Formatter, bytes: i64) -> fmt::Result {
let bytes = match bytes {
x @ 0..=i64::MAX => x as u64,
y => {
write!(fmt, "-")?;
match y.checked_abs() {
Some(abs) => abs as u64,
None => i64::MAX as u64,
}
}
};
let rule = match self.base {
Base::Base2 => match BASE2_RULES.binary_search_by_key(&bytes, |rule| rule.less_than) {
Ok(index) => &BASE2_RULES[index + 1],
Err(index) => &BASE2_RULES[index],
},
Base::Base10 => {
match BASE10_RULES.binary_search_by_key(&bytes, |rule| rule.less_than) {
Ok(index) => &BASE10_RULES[index + 1],
Err(index) => &BASE10_RULES[index],
}
}
};
(rule.formatter)(fmt, bytes)?;
rule.unit.format(fmt, bytes, &self.style)?;
Ok(())
}
}
impl SizeFormatter<()> {
pub const fn new() -> SizeFormatter<()> {
SizeFormatter {
size: (),
base: DEFAULT_BASE,
style: DEFAULT_STYLE,
}
}
pub fn format(&self, bytes: i64) -> String {
format!(
"{}",
FmtRenderer::new(|fmt: &mut fmt::Formatter| { self.inner_fmt(fmt, bytes) })
)
}
}
pub type FormattableSize<'a> = SizeFormatter<&'a Size>;
impl fmt::Display for FormattableSize<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner_fmt(f, self.size.bytes())
}
}
impl Size {
pub fn format(&self) -> FormattableSize {
FormattableSize {
size: self,
base: DEFAULT_BASE,
style: DEFAULT_STYLE,
}
}
}
struct FormatRule {
less_than: u64,
formatter: fn(&mut fmt::Formatter, bytes: u64) -> fmt::Result,
unit: Unit,
}
const BASE10_RULES: [FormatRule; 17] = [
FormatRule {
less_than: KILOBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.0}", bytes),
unit: Unit::Byte,
},
FormatRule {
less_than: 10 * KILOBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.2}", bytes as f64 / (KILOBYTE as f64)),
unit: Unit::Kilobyte,
},
FormatRule {
less_than: 100 * KILOBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.1}", bytes as f64 / (KILOBYTE as f64)),
unit: Unit::Kilobyte,
},
FormatRule {
less_than: MEGABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.0}", bytes as f64 / (KILOBYTE as f64)),
unit: Unit::Kilobyte,
},
FormatRule {
less_than: 10 * MEGABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.2}", bytes as f64 / (MEGABYTE as f64)),
unit: Unit::Megabyte,
},
FormatRule {
less_than: 100 * MEGABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.1}", bytes as f64 / (MEGABYTE as f64)),
unit: Unit::Megabyte,
},
FormatRule {
less_than: GIGABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.0}", bytes as f64 / (MEGABYTE as f64)),
unit: Unit::Megabyte,
},
FormatRule {
less_than: 10 * GIGABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.2}", bytes as f64 / (GIGABYTE as f64)),
unit: Unit::Gigabyte,
},
FormatRule {
less_than: 100 * GIGABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.1}", bytes as f64 / (GIGABYTE as f64)),
unit: Unit::Gigabyte,
},
FormatRule {
less_than: TERABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.0}", bytes as f64 / (GIGABYTE as f64)),
unit: Unit::Gigabyte,
},
FormatRule {
less_than: 10 * TERABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.2}", bytes as f64 / (TERABYTE as f64)),
unit: Unit::Terabyte,
},
FormatRule {
less_than: 100 * TERABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.1}", bytes as f64 / (TERABYTE as f64)),
unit: Unit::Terabyte,
},
FormatRule {
less_than: PETABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.0}", bytes as f64 / (TERABYTE as f64)),
unit: Unit::Terabyte,
},
FormatRule {
less_than: 10 * PETABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.2}", bytes as f64 / (PETABYTE as f64)),
unit: Unit::Petabyte,
},
FormatRule {
less_than: 100 * PETABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.1}", bytes as f64 / (PETABYTE as f64)),
unit: Unit::Petabyte,
},
FormatRule {
less_than: EXABYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.0}", bytes as f64 / (PETABYTE as f64)),
unit: Unit::Petabyte,
},
FormatRule {
less_than: u64::MAX,
formatter: |fmt, bytes| write!(fmt, "{:0}", bytes as f64 / (EXABYTE as f64)),
unit: Unit::Exabyte,
},
];
const BASE2_RULES: [FormatRule; 17] = [
FormatRule {
less_than: KIBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.0}", bytes),
unit: Unit::Byte,
},
FormatRule {
less_than: 10 * KIBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.2}", bytes as f64 / (KIBIBYTE as f64)),
unit: Unit::Kibibyte,
},
FormatRule {
less_than: 100 * KIBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.1}", bytes as f64 / (KIBIBYTE as f64)),
unit: Unit::Kibibyte,
},
FormatRule {
less_than: MEBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.0}", bytes as f64 / (KIBIBYTE as f64)),
unit: Unit::Kibibyte,
},
FormatRule {
less_than: 10 * MEBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.2}", bytes as f64 / (MEBIBYTE as f64)),
unit: Unit::Mebibyte,
},
FormatRule {
less_than: 100 * MEBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.1}", bytes as f64 / (MEBIBYTE as f64)),
unit: Unit::Mebibyte,
},
FormatRule {
less_than: GIBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.0}", bytes as f64 / (MEBIBYTE as f64)),
unit: Unit::Mebibyte,
},
FormatRule {
less_than: 10 * GIBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.2}", bytes as f64 / (GIBIBYTE as f64)),
unit: Unit::Gibibyte,
},
FormatRule {
less_than: 100 * GIBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.1}", bytes as f64 / (GIBIBYTE as f64)),
unit: Unit::Gibibyte,
},
FormatRule {
less_than: TEBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.0}", bytes as f64 / (GIBIBYTE as f64)),
unit: Unit::Gibibyte,
},
FormatRule {
less_than: 10 * TEBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.2}", bytes as f64 / (TEBIBYTE as f64)),
unit: Unit::Tebibyte,
},
FormatRule {
less_than: 100 * TEBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.1}", bytes as f64 / (TEBIBYTE as f64)),
unit: Unit::Tebibyte,
},
FormatRule {
less_than: PEBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.0}", bytes as f64 / (TEBIBYTE as f64)),
unit: Unit::Tebibyte,
},
FormatRule {
less_than: 10 * PEBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.2}", bytes as f64 / (PEBIBYTE as f64)),
unit: Unit::Pebibyte,
},
FormatRule {
less_than: 100 * PEBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.1}", bytes as f64 / (PEBIBYTE as f64)),
unit: Unit::Pebibyte,
},
FormatRule {
less_than: EXBIBYTE as u64,
formatter: |fmt, bytes| write!(fmt, "{:.0}", bytes as f64 / (PEBIBYTE as f64)),
unit: Unit::Pebibyte,
},
FormatRule {
less_than: u64::MAX,
formatter: |fmt, bytes| write!(fmt, "{:0}", bytes as f64 / (EXBIBYTE as f64)),
unit: Unit::Exbibyte,
},
];