mprober 0.10.12

This program aims to collect Linux system information including hostname, kernel version, uptime, RTC time, load average, CPU, memory, network interfaces and block devices. It can be used not only as a normal CLI tool, but also a web application with a front-end webpage and useful HTTP APIs.
use std::fmt::Write;
use std::io::{self, ErrorKind};
use std::time::Duration;

use crate::scanner_rust::{Scanner, ScannerError};

const UPTIME_PATH: &str = "/proc/uptime";

const RTC_PATH: &str = "/proc/driver/rtc";
const ITEMS: [&str; 2] = ["rtc_time", "rtc_date"];

pub fn get_uptime() -> Result<Duration, ScannerError> {
    let mut sc = Scanner::scan_path(UPTIME_PATH)?;

    let uptime = match sc.next_f64()? {
        Some(v) => v,
        None => {
            return Err(ScannerError::IOError(io::Error::new(
                ErrorKind::UnexpectedEof,
                "Cannot find the uptime.",
            )))
        }
    };

    Ok(Duration::from_secs_f64(uptime))
}

pub fn format_duration(duration: Duration) -> String {
    let sec = duration.as_secs();

    let days = sec / 86400;

    let sec = sec % 86400;

    let hours = sec / 3600;

    let sec = sec % 3600;

    let minutes = sec / 60;

    let seconds = sec % 60;

    let mut s = String::new();

    if days > 0 {
        s.write_fmt(format_args!("{} day", days)).unwrap();

        if days > 1 {
            s.push('s');
        }

        s.push_str(", ");
    }

    if hours > 0 || (days > 0) && (minutes > 0 || seconds > 0) {
        s.write_fmt(format_args!("{} hour", hours)).unwrap();

        if hours > 1 {
            s.push('s');
        }

        s.push_str(", ");
    }

    if minutes > 0 || (hours > 0 && seconds > 0) {
        s.write_fmt(format_args!("{} minute", minutes)).unwrap();

        if minutes > 1 {
            s.push('s');
        }

        s.push_str(", ");
    }

    if seconds > 0 {
        s.write_fmt(format_args!("{} second", seconds)).unwrap();

        if seconds > 1 {
            s.push('s');
        }

        s.push_str(", ");
    }

    debug_assert!(s.len() >= 2);

    if let Some(index) = s.as_str()[..(s.len() - 2)].rfind(", ") {
        s.insert_str(index + 2, "and ");
    }

    let len = s.len();

    let mut v = s.into_bytes();

    unsafe {
        v.set_len(len - 2);

        String::from_utf8_unchecked(v)
    }
}

#[derive(Debug, Clone)]
pub struct RTCDateTime {
    pub rtc_time: String,
    pub rtc_date: String,
}

impl RTCDateTime {
    pub fn get_rtc_date_time() -> Result<RTCDateTime, ScannerError> {
        let mut sc = Scanner::scan_path(RTC_PATH)?;

        let mut item_values: Vec<String> = Vec::with_capacity(ITEMS.len());

        for &item in ITEMS.iter() {
            let item_len = item.len();

            loop {
                let line = sc.next_line()?;

                match line {
                    Some(line) => {
                        if line.as_str().starts_with(item) {
                            match line[item_len..].find(':') {
                                Some(colon_index) => {
                                    let item =
                                        line[(item_len + colon_index + 1)..].trim().to_string();

                                    item_values.push(item);
                                }
                                None => {
                                    return Err(ScannerError::IOError(io::Error::new(
                                        ErrorKind::InvalidInput,
                                        format!("The item `{}` has no colon.", item),
                                    )));
                                }
                            }

                            break;
                        }
                    }
                    None => {
                        return Err(ScannerError::IOError(io::Error::new(
                            ErrorKind::UnexpectedEof,
                            format!("The item `{}` is not found.", item),
                        )));
                    }
                }
            }
        }

        let rtc_date = item_values.pop().unwrap();
        let rtc_time = item_values.pop().unwrap();

        Ok(RTCDateTime {
            rtc_time,
            rtc_date,
        })
    }
}