leenfetch_core/modules/linux/info/
battery.rs

1use crate::modules::{enums::BatteryDisplayMode, utils::get_bar};
2use std::fs;
3use std::path::Path;
4
5pub fn get_battery(display_mode: BatteryDisplayMode) -> Vec<String> {
6    collect_batteries(Path::new("/sys/class/power_supply/"), display_mode)
7}
8
9fn collect_batteries(root: &Path, display_mode: BatteryDisplayMode) -> Vec<String> {
10    let entries = match fs::read_dir(root) {
11        Ok(e) => e,
12        Err(_) => return Vec::new(),
13    };
14
15    let mut results = Vec::with_capacity(2); // most systems have max 2 batteries
16
17    for entry in entries.flatten() {
18        let name = entry.file_name();
19        let name_str = name.to_string_lossy();
20
21        // Filter battery-like names
22        if !(name_str.starts_with("BAT")
23            || name_str == "axp288_fuel_gauge"
24            || name_str.starts_with("CMB"))
25        {
26            continue;
27        }
28
29        let path = entry.path();
30
31        let capacity = match fs::read_to_string(path.join("capacity")) {
32            Ok(v) => match v.trim().parse::<u8>() {
33                Ok(num) => num,
34                Err(_) => continue,
35            },
36            Err(_) => continue,
37        };
38
39        let status = fs::read_to_string(path.join("status"))
40            .ok()
41            .map(|s| s.trim().to_string())
42            .unwrap_or_else(|| "Unknown".to_string());
43
44        let line = match display_mode {
45            BatteryDisplayMode::Bar => get_bar(capacity),
46            BatteryDisplayMode::InfoBar => {
47                format!("{}% [{}] {}", capacity, status, get_bar(capacity))
48            }
49            BatteryDisplayMode::BarInfo => {
50                format!("{} {}% [{}]", get_bar(capacity), capacity, status)
51            }
52            BatteryDisplayMode::Off => format!("{}% [{}]", capacity, status),
53        };
54
55        results.push(format!("({}): {}", name_str, line));
56    }
57
58    results
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use crate::modules::enums::BatteryDisplayMode;
65    use std::fs;
66    use std::time::{SystemTime, UNIX_EPOCH};
67
68    fn setup_battery(temp_root: &Path, name: &str, capacity: &str, status: &str) {
69        let battery_path = temp_root.join(name);
70        fs::create_dir_all(&battery_path).unwrap();
71        fs::write(battery_path.join("capacity"), capacity).unwrap();
72        fs::write(battery_path.join("status"), status).unwrap();
73    }
74
75    #[test]
76    fn collects_batteries_with_info_bar() {
77        let unique = SystemTime::now()
78            .duration_since(UNIX_EPOCH)
79            .unwrap()
80            .as_nanos();
81        let root = std::env::temp_dir().join(format!("leenfetch_battery_test_{unique}"));
82        setup_battery(&root, "BAT0", "75", "Charging");
83
84        let entries = collect_batteries(&root, BatteryDisplayMode::InfoBar);
85        assert_eq!(entries.len(), 1);
86        let info = &entries[0];
87        assert!(info.contains("(BAT0):"), "missing battery label: {info}");
88        assert!(info.contains("75%"), "missing percentage: {info}");
89        assert!(info.contains("Charging"), "missing status: {info}");
90
91        fs::remove_dir_all(root).unwrap();
92    }
93
94    #[test]
95    fn ignores_non_battery_entries() {
96        let unique = SystemTime::now()
97            .duration_since(UNIX_EPOCH)
98            .unwrap()
99            .as_nanos();
100        let root = std::env::temp_dir().join(format!("leenfetch_battery_nonbattery_{unique}"));
101        setup_battery(&root, "AC0", "100", "Full");
102        setup_battery(&root, "BAT1", "55", "Discharging");
103
104        let entries = collect_batteries(&root, BatteryDisplayMode::Off);
105        assert_eq!(entries.len(), 1);
106        assert!(
107            entries[0].contains("BAT1"),
108            "unexpected entries: {:?}",
109            entries
110        );
111
112        fs::remove_dir_all(root).unwrap();
113    }
114}