leenfetch_core/modules/linux/info/
disk.rs

1use std::process::Command;
2
3use crate::modules::{
4    enums::{DiskDisplay, DiskSubtitle},
5    utils::get_bar,
6};
7
8pub fn get_disks(
9    subtitle_mode: DiskSubtitle,
10    display_mode: DiskDisplay,
11    paths: Option<Vec<&str>>,
12) -> Option<Vec<(String, String)>> {
13    let args: Vec<&str> = {
14        let mut flags = vec!["-P", "-h"];
15        if let Some(paths) = &paths {
16            flags.extend(paths.iter());
17        } else {
18            flags.push("/");
19        }
20        flags
21    };
22
23    let output = Command::new("df").args(args).output().ok()?;
24    let stdout = String::from_utf8_lossy(&output.stdout);
25
26    Some(parse_disk_output(&stdout, subtitle_mode, display_mode))
27}
28
29fn parse_disk_output(
30    stdout: &str,
31    subtitle_mode: DiskSubtitle,
32    display_mode: DiskDisplay,
33) -> Vec<(String, String)> {
34    let mut lines = stdout.lines().skip(1);
35    let mut results = Vec::new();
36
37    while let Some(line) = lines.next() {
38        let parts: Vec<&str> = line.split_whitespace().filter(|s| !s.is_empty()).collect();
39
40        if parts.len() < 6 {
41            continue;
42        }
43
44        let total = parts[1];
45        let used = parts[2];
46        let perc = parts[4].trim_end_matches('%');
47        let mount = parts[5];
48
49        let usage_display = format!("{} / {}", used, total);
50        let perc_val = perc.parse::<u8>().unwrap_or(0);
51
52        let final_str = match display_mode {
53            DiskDisplay::Info => usage_display,
54            DiskDisplay::Percentage => format!("{}% {}", perc, get_bar(perc_val)),
55            DiskDisplay::InfoBar => format!("{} {}", usage_display, get_bar(perc_val)),
56            DiskDisplay::BarInfo => format!("{} {}", get_bar(perc_val), usage_display),
57            DiskDisplay::Bar => get_bar(perc_val),
58        };
59
60        let subtitle = match subtitle_mode {
61            DiskSubtitle::Name => parts[0].to_string(),
62            DiskSubtitle::Dir => mount.split('/').last().unwrap_or("").to_string(),
63            DiskSubtitle::None => "".to_string(),
64            DiskSubtitle::Mount => mount.to_string(),
65        };
66
67        let full_subtitle = if subtitle.is_empty() {
68            "Disk".to_string()
69        } else {
70            format!("Disk ({})", subtitle)
71        };
72
73        results.push((full_subtitle, final_str));
74    }
75
76    results
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    fn sample_df_output() -> &'static str {
84        r#"Filesystem     Size  Used Avail Use% Mounted on
85/dev/sda1       100G   40G   60G  40% /
86tmpfs           2.0G     0  2.0G   0% /dev/shm
87/dev/sdb1       200G  150G   50G  75% /mnt/data
88"#
89    }
90
91    #[test]
92    fn test_parse_info_display_mount_subtitle() {
93        let result = parse_disk_output(sample_df_output(), DiskSubtitle::Mount, DiskDisplay::Info);
94
95        assert_eq!(result.len(), 3);
96        assert_eq!(result[0].0, "Disk (/)");
97        assert_eq!(result[0].1, "40G / 100G");
98    }
99
100    #[test]
101    fn test_parse_percentage_display_dir_subtitle() {
102        let result = parse_disk_output(
103            sample_df_output(),
104            DiskSubtitle::Dir,
105            DiskDisplay::Percentage,
106        );
107
108        assert_eq!(result[2].0, "Disk (data)");
109        assert!(result[2].1.starts_with("75%"));
110    }
111
112    #[test]
113    fn test_parse_bar_only() {
114        let result = parse_disk_output(sample_df_output(), DiskSubtitle::None, DiskDisplay::Bar);
115
116        assert_eq!(result[0].0, "Disk");
117        assert!(result[0].1.starts_with("[") && result[0].1.ends_with("]"));
118        assert!(result[0].1.len() > 2); // should contain some kind of bar
119    }
120
121    #[test]
122    fn test_parse_barinfo_with_name() {
123        let result =
124            parse_disk_output(sample_df_output(), DiskSubtitle::Name, DiskDisplay::BarInfo);
125
126        assert_eq!(result[2].0, "Disk (/dev/sdb1)");
127        assert!(result[2].1.contains("150G / 200G"));
128    }
129}