Skip to main content

rubblets/
device.rs

1use crate::RubbleResult;
2use clap::Args;
3use clap::ValueEnum;
4use handlebars::JsonValue;
5use serde_json::json;
6use std::error::Error;
7use std::thread;
8use std::time::Duration;
9use systemstat::{Platform, System};
10
11#[derive(Args)]
12pub struct DeviceArgs {
13    pub selector: SystemStats,
14}
15
16#[derive(Copy, Clone, ValueEnum)]
17pub enum SystemStats {
18    /// CPU Stats
19    CPU,
20    /// Load Avg
21    LoadAvg,
22    /// Temp
23    Temp,
24    /// Time Machine last booted
25    BootTime,
26    /// Battery info
27    Battery,
28}
29
30impl SystemStats {
31    pub fn default_format(&self) -> &'static str {
32        match self {
33            SystemStats::CPU => "{{total}}%",
34            SystemStats::LoadAvg => "{{load_avg_1}}, {{load_avg_5}}, {{load_avg_15}}",
35            SystemStats::Temp => "{{temp}}",
36            SystemStats::BootTime => "{{boot_time}}",
37            SystemStats::Battery => "{{charge}}%",
38        }
39    }
40
41    pub fn formatting_strings(&self) -> &'static str {
42        match self {
43            SystemStats::CPU => "total user nice system interrupt",
44            SystemStats::LoadAvg => "load_avg_1 load_avg_5 load_avg_15",
45            SystemStats::Temp => "temp",
46            SystemStats::BootTime => "boot_time",
47            SystemStats::Battery => "charge time_remaining",
48        }
49    }
50
51    pub fn to_result(&self) -> RubbleResult {
52        let sys = System::new();
53        let r = match self {
54            SystemStats::CPU => cpu_stat(sys),
55            SystemStats::LoadAvg => load_avg(sys),
56            SystemStats::Temp => temp(sys),
57            SystemStats::BootTime => boot_time(sys),
58            SystemStats::Battery => battery(sys),
59        };
60        RubbleResult::new(r, self.default_format())
61    }
62}
63
64fn battery(sys: System) -> Result<JsonValue, Box<dyn Error>> {
65    match sys.battery_life() {
66        Ok(battery) => Ok(json!({
67            "charge": battery.remaining_capacity * 100.0,
68            "time_remaining": format!(
69              "{}h{}m",
70              battery.remaining_time.as_secs() / 3600,
71              battery.remaining_time.as_secs() % 60
72            )
73
74        })),
75        Err(e) => Err(e.into()),
76    }
77}
78
79fn cpu_stat(sys: System) -> Result<JsonValue, Box<dyn Error>> {
80    match sys.cpu_load_aggregate() {
81        Ok(cpu) => {
82            thread::sleep(Duration::from_secs(1));
83            let cpu = cpu.done().unwrap();
84            let u = cpu.user * 100.00;
85            let n = cpu.nice * 100.00;
86            let s = cpu.system * 100.00;
87            let i = cpu.interrupt * 100.00;
88            let t = u + n + s + i;
89            Ok(json!({ "total": t, "user": u, "nice": n, "system": s, "interrupt": i }))
90        }
91        Err(e) => Err(e.into()),
92    }
93}
94
95fn load_avg(sys: System) -> Result<JsonValue, Box<dyn Error>> {
96    match sys.load_average() {
97        Ok(loadavg) => Ok(json!({
98          "load_avg_1": loadavg.one,
99          "load_avg_5": loadavg.five,
100          "load_avg_15": loadavg.fifteen,
101        })),
102        Err(e) => Err(e.into()),
103    }
104}
105
106fn temp(sys: System) -> Result<JsonValue, Box<dyn Error>> {
107    match sys.cpu_temp() {
108        Ok(cpu_temp) => Ok(json!({ "temp": cpu_temp })),
109        Err(e) => Err(e.into()),
110    }
111}
112
113fn boot_time(sys: System) -> Result<JsonValue, Box<dyn Error>> {
114    match sys.boot_time() {
115        Ok(boot_time) => Ok(json!({ "boot_time": boot_time.to_string() })),
116        Err(x) => Err(x.into()),
117    }
118}