Skip to main content

cargo_plot/core/file_stats/
weight.rs

1// [ENG]: Logic for calculating and formatting file and directory weights.
2// [POL]: Logika obliczania i formatowania wag plików oraz folderów.
3
4use std::fs;
5use std::path::Path;
6
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub enum UnitSystem {
9    Decimal,
10    Binary,
11    Both,
12    None,
13}
14
15#[derive(Debug, Clone)]
16pub struct WeightConfig {
17    pub system: UnitSystem,
18    pub precision: usize,
19    pub show_for_files: bool,
20    pub show_for_dirs: bool,
21    pub dir_sum_included: bool,
22}
23
24impl Default for WeightConfig {
25    fn default() -> Self {
26        Self {
27            system: UnitSystem::Decimal,
28            precision: 5,
29            show_for_files: true,
30            show_for_dirs: true,
31            dir_sum_included: true,
32        }
33    }
34}
35
36/// [POL]: Pobiera wagę ścieżki (plik lub folder rekurencyjnie).
37pub fn get_path_weight(path: &Path, sum_included_only: bool) -> u64 {
38    let metadata = match fs::metadata(path) {
39        Ok(m) => m,
40        Err(_) => return 0,
41    };
42
43    if metadata.is_file() {
44        return metadata.len();
45    }
46
47    // ⚡ Jeśli sum_included_only jest false (flaga -a), liczymy rekurencyjnie fizyczny rozmiar
48    if metadata.is_dir() && !sum_included_only {
49        return get_dir_size(path);
50    }
51
52    0
53}
54
55/// [POL]: Prywatny pomocnik do liczenia rozmiaru folderu na dysku.
56fn get_dir_size(path: &Path) -> u64 {
57    fs::read_dir(path)
58        .map(|entries| {
59            entries
60                .filter_map(Result::ok)
61                .map(|e| {
62                    let p = e.path();
63                    if p.is_dir() {
64                        get_dir_size(&p)
65                    } else {
66                        e.metadata().map(|m| m.len()).unwrap_or(0)
67                    }
68                })
69                .sum()
70        })
71        .unwrap_or(0)
72}
73
74/// [POL]: Formatuje bajty na czytelny ciąg znaków (np. [kB 12.34]).
75pub fn format_weight(bytes: u64, is_dir: bool, config: &WeightConfig) -> String {
76    if config.system == UnitSystem::None {
77        return String::new();
78    }
79
80    let should_show = (is_dir && config.show_for_dirs) || (!is_dir && config.show_for_files);
81    if !should_show {
82        let empty_width = 7 + config.precision;
83        return format!("{:width$}", "", width = empty_width);
84    }
85
86    let (base, units) = match config.system {
87        UnitSystem::Binary => (1024.0_f64, vec!["B", "KiB", "MiB", "GiB", "TiB", "PiB"]),
88        _ => (1000.0_f64, vec!["B", "kB", "MB", "GB", "TB", "PB"]),
89    };
90
91    if bytes == 0 {
92        return format!(
93            "[{:>3} {:>width$}] ",
94            units[0],
95            "0",
96            width = config.precision
97        );
98    }
99
100    let bytes_f = bytes as f64;
101    let exp = (bytes_f.ln() / base.ln()).floor() as usize;
102    let exp = exp.min(units.len() - 1);
103    let value = bytes_f / base.powi(exp as i32);
104    let unit = units[exp];
105
106    let mut formatted_value = format!("{value:.10}");
107    if formatted_value.len() > config.precision {
108        formatted_value = formatted_value[..config.precision]
109            .trim_end_matches('.')
110            .to_string();
111    } else {
112        formatted_value = format!("{formatted_value:>width$}", width = config.precision);
113    }
114
115    format!("[{unit:>3} {formatted_value}] ")
116}