oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
2,932 bytes raw
1
use super::Block;
2
use crate::errors::BlockError;
3
use std::fs;
4
use std::path::{Path, PathBuf};
5
use std::time::Duration;
6
7
pub struct Battery {
8
    format_charging: String,
9
    format_discharging: String,
10
    format_full: String,
11
    interval: Duration,
12
    color: u32,
13
    battery_path: String,
14
}
15
16
fn detect_battery_name() -> Option<String> {
17
    let base = Path::new("/sys/class/power_supply");
18
    let entries = fs::read_dir(base).ok()?;
19
20
    for entry in entries.flatten() {
21
        let path: PathBuf = entry.path();
22
23
        let type_path = path.join("type");
24
        let present_path = path.join("present");
25
26
        let is_battery = fs::read_to_string(&type_path)
27
            .map(|s| s.trim() == "Battery")
28
            .unwrap_or(false);
29
30
        let is_present = fs::read_to_string(&present_path)
31
            .map(|s| s.trim() == "1")
32
            .unwrap_or(true);
33
34
        let is_device_scope = fs::read_to_string(path.join("scope"))
35
            .map(|s| s.trim().eq_ignore_ascii_case("device"))
36
            .unwrap_or(false);
37
38
        if !is_battery || !is_present || is_device_scope {
39
            continue;
40
        }
41
42
        if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
43
            return Some(name.to_string());
44
        }
45
    }
46
    None
47
}
48
49
impl Battery {
50
    pub fn new(
51
        format_charging: &str,
52
        format_discharging: &str,
53
        format_full: &str,
54
        interval_secs: u64,
55
        color: u32,
56
        battery_name: Option<String>,
57
    ) -> Self {
58
        let name = battery_name
59
            .or_else(detect_battery_name)
60
            .unwrap_or_else(|| "BAT0".to_string());
61
62
        Self {
63
            format_charging: format_charging.to_string(),
64
            format_discharging: format_discharging.to_string(),
65
            format_full: format_full.to_string(),
66
            interval: Duration::from_secs(interval_secs),
67
            color,
68
            battery_path: format!("/sys/class/power_supply/{}", name),
69
        }
70
    }
71
72
    fn read_file(&self, filename: &str) -> Result<String, BlockError> {
73
        let path = format!("{}/{}", self.battery_path, filename);
74
        Ok(fs::read_to_string(path)?.trim().to_string())
75
    }
76
77
    fn get_capacity(&self) -> Result<u32, BlockError> {
78
        Ok(self.read_file("capacity")?.parse()?)
79
    }
80
81
    fn get_status(&self) -> Result<String, BlockError> {
82
        self.read_file("status")
83
    }
84
}
85
86
impl Block for Battery {
87
    fn content(&mut self) -> Result<String, BlockError> {
88
        let capacity = self.get_capacity()?;
89
        let status = self.get_status()?;
90
91
        let format = match status.as_str() {
92
            "Charging" => &self.format_charging,
93
            "Full" => &self.format_full,
94
            _ => &self.format_discharging,
95
        };
96
97
        Ok(format.replace("{}", &capacity.to_string()))
98
    }
99
100
    fn interval(&self) -> Duration {
101
        self.interval
102
    }
103
104
    fn color(&self) -> u32 {
105
        self.color
106
    }
107
}