cargo_plot/core/file_stats/
weight.rs1use 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
36pub 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 if metadata.is_dir() && !sum_included_only {
49 return get_dir_size(path);
50 }
51
52 0
53}
54
55fn 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
74pub 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}