mod download_acceleration;
mod download_speed;
mod file_size;
use std::fmt::{self, Display};
pub use download_acceleration::DownloadAcceleration;
pub use download_speed::DownloadSpeed;
pub use file_size::{FileSize, NonZeroFileSize};
pub(crate) const SCALE: u128 = 1_000_000;
pub(crate) const SCALE_I128: i128 = 1_000_000;
pub trait FileSizeFormat: Sized {
fn get_si_parts(&self) -> (String, &'static str);
fn get_iec_parts(&self) -> (String, &'static str);
#[inline]
fn to_si_string(&self) -> String {
let (value, unit) = self.get_si_parts();
format!("{} {}", value, unit)
}
#[inline]
fn to_iec_string(&self) -> String {
let (value, unit) = self.get_iec_parts();
format!("{} {}", value, unit)
}
#[inline]
fn to_formatted(self, standard: SizeStandard) -> FormattedValue<Self> {
FormattedValue::new(self, standard)
}
}
pub struct FormattedValue<T: FileSizeFormat> {
value: T,
standard: SizeStandard,
}
impl<T: FileSizeFormat> FormattedValue<T> {
pub fn new(value: T, standard: SizeStandard) -> Self {
Self { value, standard }
}
pub fn get_value(&self) -> &T {
&self.value
}
}
impl<T: FileSizeFormat> Display for FormattedValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let display = match self.standard {
SizeStandard::SI => self.value.to_si_string(),
SizeStandard::IEC => self.value.to_iec_string(),
};
write!(f, "{}", display)
}
}
#[derive(Debug, Clone, Copy)]
pub enum SizeStandard {
SI,
IEC,
}
pub(crate) fn format_parts_scaled(
mut scaled_value: u128,
base: u128,
units: &'static [&'static str],
) -> (String, &'static str) {
let mut unit_index = 0;
let base_scaled = base * SCALE;
while scaled_value >= base_scaled && unit_index < units.len() - 1 {
scaled_value = scaled_value * SCALE / base_scaled;
unit_index += 1;
}
let formatted_value = format_scaled_value(scaled_value);
(formatted_value, units[unit_index])
}
pub(crate) fn format_parts_scaled_signed(
scaled_value: i128,
base: u128,
units: &'static [&'static str],
) -> (String, &'static str) {
let is_negative = scaled_value < 0;
let abs_value = scaled_value.unsigned_abs();
let (formatted, unit) = format_parts_scaled(abs_value, base, units);
if is_negative {
(format!("-{}", formatted), unit)
} else {
(formatted, unit)
}
}
#[inline]
pub(crate) fn format_scaled_value(scaled_value: u128) -> String {
let integer_part = scaled_value / SCALE;
let fractional_part = scaled_value % SCALE;
if integer_part < 10 {
let hundredths = (fractional_part + 5000) / 10000; let hundredths = if hundredths >= 100 {
return format!("{:.2}", (integer_part + 1) as f64);
} else {
hundredths
};
format!("{}.{:02}", integer_part, hundredths)
} else {
let tenths = (fractional_part + 50000) / 100000; if tenths >= 10 {
format!("{}.0", integer_part + 1)
} else {
format!("{}.{}", integer_part, tenths)
}
}
}