Skip to main content

lib/
fn_weight.rs

1use std::fs;
2use std::path::Path;
3
4#[derive(Debug, Clone, Copy, PartialEq)]
5pub enum UnitSystem {
6    Decimal, // 1000^n (kB, MB...)
7    Binary,  // 1024^n (KiB, MiB...)
8    Both,
9    None,
10}
11
12#[derive(Debug, Clone)]
13pub struct WeightConfig {
14    pub system: UnitSystem,
15    pub precision: usize, // Całkowita szerokość pola "xxxxx" (min 3)
16    pub show_for_files: bool,
17    pub show_for_dirs: bool,
18    pub dir_sum_included: bool, // true = tylko uwzględnione, false = rzeczywista waga folderu
19}
20
21impl Default for WeightConfig {
22    fn default() -> Self {
23        Self {
24            system: UnitSystem::Decimal,
25            precision: 5,
26            show_for_files: true,
27            show_for_dirs: true,
28            dir_sum_included: true,
29        }
30    }
31}
32
33/// Główna funkcja formatująca wagę do postaci [qq xxxxx]
34pub fn format_weight(bytes: u64, config: &WeightConfig) -> String {
35    if config.system == UnitSystem::None {
36        return String::new();
37    }
38
39    let (base, units) = match config.system {
40        UnitSystem::Binary => (1024.0_f64, vec!["B", "KiB", "MiB", "GiB", "TiB", "PiB"]),
41        _ => (1000.0_f64, vec!["B", "kB", "MB", "GB", "TB", "PB"]),
42    };
43
44    if bytes == 0 {
45        return format!(
46            "[{:>2} {:>width$}] ",
47            units[0],
48            "0",
49            width = config.precision
50        );
51    }
52
53    let bytes_f = bytes as f64;
54    let exp = (bytes_f.ln() / base.ln()).floor() as usize;
55    let exp = exp.min(units.len() - 1);
56    let value = bytes_f / base.powi(exp as i32);
57    let unit = units[exp];
58
59    // Formatowanie liczby do stałej szerokości "xxxxx"
60    let formatted_value = format_value_with_precision(value, config.precision);
61
62    format!("[{:>3} {}] ", unit, formatted_value)
63}
64
65fn format_value_with_precision(value: f64, width: usize) -> String {
66    // Sprawdzamy ile cyfr ma część całkowita
67    let integer_part = value.floor() as u64;
68    let integer_str = integer_part.to_string();
69    let int_len = integer_str.len();
70
71    if int_len >= width {
72        // Jeśli sama liczba całkowita zajmuje całe miejsce lub więcej
73        return integer_str[..width].to_string();
74    }
75
76    // Obliczamy ile miejsc po przecinku nam zostało (width - int_len - 1 dla kropki)
77    let available_precision = if width > int_len + 1 {
78        width - int_len - 1
79    } else {
80        0
81    };
82
83    let formatted = format!("{:.1$}", value, available_precision);
84
85    // Na wypadek zaokrągleń (np. 99.99 -> 100.0), przycinamy do width
86    if formatted.len() > width {
87        formatted[..width].trim_end_matches('.').to_string()
88    } else {
89        format!("{:>width$}", formatted, width = width)
90    }
91}
92
93/// Pobiera wagę pliku lub folderu (rekurencyjnie)
94pub fn get_path_weight(path: &Path, sum_included_only: bool) -> u64 {
95    let metadata = match fs::metadata(path) {
96        Ok(m) => m,
97        Err(_) => return 0,
98    };
99
100    if metadata.is_file() {
101        return metadata.len();
102    }
103
104    if metadata.is_dir() && !sum_included_only {
105        // Rzeczywista waga folderu na dysku
106        return get_dir_size(path);
107    }
108
109    0 // Jeśli liczymy tylko sumę plików, bazowo folder ma 0 (sumowanie nastąpi w drzewie)
110}
111
112fn get_dir_size(path: &Path) -> u64 {
113    fs::read_dir(path)
114        .map(|entries| {
115            entries
116                .filter_map(|e| e.ok())
117                .map(|e| {
118                    let p = e.path();
119                    if p.is_dir() {
120                        get_dir_size(&p)
121                    } else {
122                        e.metadata().map(|m| m.len()).unwrap_or(0)
123                    }
124                })
125                .sum()
126        })
127        .unwrap_or(0)
128}