~humaid/hstatus

c03242bd12109d293a8a7602ea34ba023723ddcf — Humaid AlQassimi 2 months ago 1ee4366
Add comments, remove covid stats
2 files changed, 36 insertions(+), 47 deletions(-)

M Cargo.toml
M src/main.rs
M Cargo.toml => Cargo.toml +0 -2
@@ 10,7 10,5 @@ edition = "2018"
x11 = { version = "2.18.2", features = ["xlib"] }
chrono = "0.4"
libc = "0.2.68"
reqwest = { version = "0.10.9", features = ["blocking"] }
select = "0.6.0-alpha.1"
cached = "0.22.0"
signal-hook = "0.2.1"

M src/main.rs => src/main.rs +36 -45
@@ 1,14 1,10 @@
extern crate chrono;
extern crate libc;
extern crate x11;
extern crate reqwest;
extern crate select;
extern crate cached;
extern crate signal_hook;

use signal_hook::{iterator::Signals, SIGINT};

use cached::proc_macro::cached;
use signal_hook::{iterator::Signals, SIGINT, SIGHUP};

use select::document::Document;
use select::predicate::{Class, Predicate};


@@ 22,19 18,26 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::{string, thread, time};

/// System's name
const SYSTEM_NAME: &str = "humaid's system";

/// DesktopStatus opens the X Display to allow changing the status (root
/// window) name.
#[derive(Debug, Clone, Copy)]
pub struct DesktopStatus {
    disp: *mut x11::xlib::Display,
}

impl DesktopStatus {
    /// Initialises a new DesktopStatus, gets the pointer to the X Display.
    pub fn new() -> Self {
        DesktopStatus {
            disp: unsafe { x11::xlib::XOpenDisplay(std::ptr::null()) },
        }
    }

    /// Sets the name of the root window. This will change the status bar in
    /// dwm.
    pub fn set_status(self, stat: &str) {
        unsafe {
            let s = CString::new(stat).expect("CString::new failed at set_status");


@@ 46,38 49,43 @@ impl DesktopStatus {
            x11::xlib::XSync(self.disp, 0);
        }
    }
    pub fn close(self) {
    
    /// Closes the X Display. This needs to be called once finished using this
    /// structure.
    pub fn close(self) { // TODO destructor
        unsafe {
            x11::xlib::XCloseDisplay(self.disp);
        }
    }
}

type StatusItem = fn() -> String;
/// This represents a function which returns a status item.
type StatusItem = fn() -> Result<String, String>;

fn load_item() -> String {
/// A StatusItem which displays the system load.
fn load_item() -> Result<String, String> {
    match get_load() {
        Ok(load) => return format!("L:{}|", load.as_str()),
        Err(why) => println!("Cannot get load: {}", why),
        Ok(load) => return Ok(format!("L:{}|", load.as_str())),
        Err(why) => Err(format!("Cannot get load: {}", why)),
    }
    "".to_string()
}

fn battery_item() -> String {
/// A StatusItem which displays the battery percentage.
fn battery_item() -> Result<String, String> {
    match get_battery_with_status() {
        Ok(perc) => return format!("B:{}|", perc.as_str()),
        Err(why) => println!("Cannot get battery percentage: {}", why),
        Ok(perc) => return Ok(format!("B:{}|", perc.as_str())),
        Err(why) => Err(format!("Cannot get battery percentage: {}", why)),
    }
    "".to_string()
}

fn time_item() -> String {
/// A StatusItem which displays the time and date.
fn time_item() -> Result<String, String> {
    let mut res = String::new();
    let local: DateTime<Local> = Local::now();
    res.push_str("UK:");
    res.push_str(
        local
            //.with_timezone(&chrono::FixedOffset::east(3600))
            .with_timezone(&chrono::FixedOffset::east(3600))
            .format("%I:%M")
            .to_string()
            .as_str(),


@@ 88,38 96,14 @@ fn time_item() -> String {
        .with_timezone(&chrono::FixedOffset::east(4 * 3600))
        .format("%I:%M %p %d-%m-%Y").to_string().as_str());
    res.push('|');
    res
}

#[cached(time=21600)]
fn get_covid_stats() -> String {
    let resp = reqwest::blocking::get("http://covid19.ncema.gov.ae/").unwrap();
    if !resp.status().is_success() {
        println!("covid fail: {}", resp.status());
        return "fail".to_string();
    }
    let doc = Document::from_read(resp).unwrap();

    let active = doc
        .select(Class("active").descendant(Class("counter"))).next().unwrap()
        .text();
    
    let recovered = doc
        .select(Class("recovered").descendant(Class("new-cases").descendant(Class("recovered")))).next().unwrap()
        .text();

    let rec = recovered.split(' ').collect::<Vec<&str>>()[2].to_string();

    return format!("A:{} R:{}|", active, rec);
    Ok(res)
}

fn main() {
    let signals = Signals::new(&[SIGINT]).unwrap();

    let signals = Signals::new(&[SIGINT, SIGHUP]).unwrap();

    // Register the status items
    let mut stat_items: Vec<StatusItem> = Vec::new();
    stat_items.push(get_covid_stats);
    stat_items.push(load_item);
    stat_items.push(battery_item);
    stat_items.push(time_item);


@@ 127,11 111,11 @@ fn main() {
    let status: DesktopStatus = DesktopStatus::new();

    let cont = Arc::new(AtomicBool::new(true));

    {
        let c = Arc::clone(&cont);
        thread::spawn(move || {
            for _ in signals.forever() {
                println!("Closing...");
                c.store(false, Ordering::SeqCst);
            }
        });


@@ 166,7 150,10 @@ fn main() {
        let mut stat = String::new();

        for i in &stat_items {
            stat.push_str(i().as_str());
            match i() {
                Ok(s) => stat.push_str(s.as_str()),
                Err(s) => println!("Error: {}", s),
            }
        }

        stat.push_str(SYSTEM_NAME);


@@ 176,6 163,7 @@ fn main() {
    status.close();
}

/// Reads a file from the system.
fn read_file(file: &str) -> std::io::Result<string::String> {
    let mut file = File::open(file)?;
    let mut contents = String::new();


@@ 183,6 171,7 @@ fn read_file(file: &str) -> std::io::Result<string::String> {
    Ok(contents.replace("\n", ""))
}

/// Gets the system load, formatted in a way similar to uptime.
fn get_load() -> Result<string::String, &'static str> {
    let mut avgs = Vec::with_capacity(3_usize);
    match unsafe { getloadavg(avgs.as_mut_ptr(), 3 as c_int) } {


@@ 197,6 186,7 @@ fn get_load() -> Result<string::String, &'static str> {
    }
}

/// Gets the battery percentage.
fn get_battery_perc() -> i32 {
    let present = read_file("/sys/class/power_supply/BAT0/present").unwrap();
    assert_eq!(present, "1", "Battery not present");


@@ 209,6 199,7 @@ fn get_battery_perc() -> i32 {
    return ((now as f64 / full as f64) * 100_f64) as i32
}

/// Gets the charging/discharing status.
fn get_battery_with_status() -> std::io::Result<string::String> {
    let status = read_file("/sys/class/power_supply/BAT0/status")?;
    let stat = match status.as_ref() {