proc_getter 0.0.3

make retriving infomations from /proc/* easier
Documentation
use crate::{Error, Result};
use std::str::FromStr;

/// represent the content of /proc/stat
#[derive(Debug)]
pub struct Stat {
    cpu: Cpu,
    cpu_n: Vec<Cpu>,
    intr: Vec<usize>,
    ctxt: usize,
    btime: usize,
    procs_running: usize,
    processes: usize,
    procs_blocked: usize,
    softirq: Vec<usize>,
}

impl Stat {
    getter_gen! {
        cpu: Cpu,
        cpu_n: Vec<Cpu>,
        intr: Vec<usize>,
        ctxt: usize,
        btime: usize,
        procs_running: usize,
        processes: usize,
        procs_blocked: usize,
        softirq: Vec<usize>
    }
}

#[derive(Debug)]
pub struct Cpu {
    user: usize,
    nice: usize,
    system: usize,
    idle: usize,
    iowait: usize,
    irq: usize,
    softirq: usize,
    steal: usize,
    guest: usize,
    guest_nice: usize,
}

impl Cpu {
    getter_gen! {
        user: usize,
        nice: usize,
        system: usize,
        idle: usize,
        iowait: usize,
        irq: usize,
        softirq: usize,
        steal: usize,
        guest: usize,
        guest_nice: usize
    }

    /// return the cpu time.
    ///
    /// user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice
    pub fn cpu_time(&self) -> usize {
        self.user
            + self.nice
            + self.system
            + self.idle
            + self.iowait
            + self.irq
            + self.steal
            + self.guest
            + self.guest_nice
    }

    /// return the user time.
    ///
    /// user + nice
    pub fn user_time(&self) -> usize {
        self.user + self.nice
    }

    /// return the system time.
    ///
    /// system + irq + softirq
    pub fn system_time(&self) -> usize {
        self.system + self.irq + self.softirq
    }
}

impl From<[usize; 10]> for Cpu {
    fn from(x: [usize; 10]) -> Cpu {
        Cpu {
            user: x[0],
            nice: x[1],
            system: x[2],
            idle: x[3],
            iowait: x[4],
            irq: x[5],
            softirq: x[6],
            steal: x[7],
            guest: x[8],
            guest_nice: x[9],
        }
    }
}

impl FromStr for Stat {
    type Err = Error;

    fn from_str(s: &str) -> Result<Stat> {
        let lines: Vec<&str> = s.trim().lines().collect();
        let line_num = lines.len();
        if line_num < 8 {
            return Err(Error::BadFormat);
        }

        let mut cpu_arr = [0; 10];
        let cpu_columns: Vec<&str> = lines[0].split_ascii_whitespace().collect();
        if cpu_columns.len() != 11 || cpu_columns[0] != "cpu" {
            return Err(Error::BadFormat);
        }
        for (i, v) in cpu_columns[1..].iter().enumerate() {
            let n = v.parse::<usize>()?;
            cpu_arr[i] = n;
        }
        let cpu = Cpu::from(cpu_arr);

        let mut last_n = line_num - 1;

        let softirq_columns: Vec<&str> = lines[last_n].split_ascii_whitespace().collect();
        if softirq_columns[0] != "softirq" {
            return Err(Error::BadFormat);
        }
        let mut softirq: Vec<usize> = vec![];
        for s in &softirq_columns[1..] {
            let n = s.parse::<usize>()?;
            softirq.push(n);
        }
        last_n -= 1;

        let procs_blocked = lines[last_n]
            .trim_start_matches("procs_blocked")
            .trim()
            .parse::<usize>()?;
        last_n -= 1;

        let procs_running = lines[last_n]
            .trim_start_matches("procs_running")
            .trim()
            .parse::<usize>()?;
        last_n -= 1;

        let processes = lines[last_n]
            .trim_start_matches("processes")
            .trim()
            .parse::<usize>()?;
        last_n -= 1;

        let btime = lines[last_n]
            .trim_start_matches("btime")
            .trim()
            .parse::<usize>()?;
        last_n -= 1;

        let ctxt = lines[last_n]
            .trim_start_matches("ctxt")
            .trim()
            .parse::<usize>()?;
        last_n -= 1;

        let mut intr: Vec<usize> = vec![];
        for s in lines[last_n]
            .trim_start_matches("intr")
            .split_ascii_whitespace()
        {
            let n = s.parse::<usize>()?;
            intr.push(n);
        }
        last_n -= 1;

        let mut cpu_n = Vec::with_capacity(last_n);

        for i in 0..last_n {
            let mut cpu_arr = [0; 10];
            let columns: Vec<&str> = lines[i + 1]
                .trim_start_matches("cpu")
                .split_ascii_whitespace()
                .collect();
            if columns.len() != 11 || columns[0] != i.to_string() {
                return Err(Error::BadFormat);
            }
            for (k, v) in columns[1..].iter().enumerate() {
                cpu_arr[k] = v.parse::<usize>()?;
            }
            cpu_n.push(Cpu::from(cpu_arr));
        }

        Ok(Stat {
            cpu,
            cpu_n,
            intr,
            ctxt,
            btime,
            processes,
            procs_running,
            procs_blocked,
            softirq,
        })
    }
}

pub fn stat() -> Result<Stat> {
    let content = std::fs::read_to_string("/proc/stat")?;
    Stat::from_str(&content)
}