net-bytes 0.3.0

A Rust library for handling file sizes, download speeds, and download acceleration with support for both SI and IEC standards
Documentation
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};

/// Scale factor for fixed-point arithmetic (6 decimal places)
/// 
/// 定点数的缩放因子(6位小数精度)
pub(crate) const SCALE: u128 = 1_000_000;
pub(crate) const SCALE_I128: i128 = 1_000_000;

/// A trait for types that can be formatted as file sizes
///
/// 可以格式化为文件大小的 trait
pub trait FileSizeFormat: Sized {
    /// Returns the formatted value and unit in SI (base-1000) standard
    ///
    /// 返回 SI (base-1000) 标准的 (formatted_value, unit)
    fn get_si_parts(&self) -> (String, &'static str);

    /// Returns the formatted value and unit in IEC (base-1024) standard
    ///
    /// 返回 IEC (base-1024) 标准的 (formatted_value, unit)
    fn get_iec_parts(&self) -> (String, &'static str);

    /// Returns a formatted string in SI (base-1000) standard
    ///
    /// 返回 SI (base-1000) 标准的格式化字符串
    #[inline]
    fn to_si_string(&self) -> String {
        let (value, unit) = self.get_si_parts();
        format!("{} {}", value, unit)
    }

    /// Returns a formatted string in IEC (base-1024) standard
    ///
    /// 返回 IEC (base-1024) 标准的格式化字符串
    #[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)
    }
}

/// Standard for file size units
///
/// 文件大小单位标准
#[derive(Debug, Clone, Copy)]
pub enum SizeStandard {
    /// SI standard (base-1000, units: KB, MB)
    ///
    /// SI 标准 (base-1000, 单位 KB, MB)
    SI,
    /// IEC standard (base-1024, units: KiB, MiB)
    ///
    /// IEC 标准 (base-1024, 单位 KiB, MiB)
    IEC,
}

/// Format a scaled fixed-point value and return (formatted_value, unit)
/// 
/// The input `scaled_value` should be the actual value multiplied by SCALE.
///
/// 格式化定点数值,返回 (formatted_value, unit)
/// 
/// 输入的 `scaled_value` 应该是实际值乘以 SCALE。
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 / base (keeping the scale)
        scaled_value = scaled_value * SCALE / base_scaled;
        unit_index += 1;
    }

    let formatted_value = format_scaled_value(scaled_value);
    (formatted_value, units[unit_index])
}

/// Format a scaled fixed-point value for signed types (acceleration)
///
/// 格式化有符号定点数值(用于加速度)
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)
    }
}

/// Format a scaled value with automatic decimal places selection
/// - Less than 10: 2 decimal places
/// - 10 or greater: 1 decimal place
///
/// 格式化缩放后的值,根据值的大小自动选择小数位数
/// - 小于10: 显示2位小数
/// - 大于等于10: 显示1位小数
#[inline]
pub(crate) fn format_scaled_value(scaled_value: u128) -> String {
    // Convert from SCALE (1_000_000) to the display format
    let integer_part = scaled_value / SCALE;
    let fractional_part = scaled_value % SCALE;
    
    if integer_part < 10 {
        // 2 decimal places: round to nearest 0.01
        // fractional_part is in millionths, we need hundredths
        let hundredths = (fractional_part + 5000) / 10000; // round to 2 decimal places
        let hundredths = if hundredths >= 100 {
            // Carry over to integer part handled by checking
            return format!("{:.2}", (integer_part + 1) as f64);
        } else {
            hundredths
        };
        format!("{}.{:02}", integer_part, hundredths)
    } else {
        // 1 decimal place: round to nearest 0.1
        let tenths = (fractional_part + 50000) / 100000; // round to 1 decimal place
        if tenths >= 10 {
            format!("{}.0", integer_part + 1)
        } else {
            format!("{}.{}", integer_part, tenths)
        }
    }
}