fxkit 0.1.3

Useful utilities for writting Rust CLI tools
Documentation
use colored::Colorize;
use std::path::Path;

use regex::Regex;

#[derive(Debug)]
pub enum OSErrors {
    NoSuchFile,
    FailedToRead,
    RegexError,
}

pub const OS_RELEASE_PATHS: [&str; 3] = ["/bedrock/etc/os-release", "/etc/os-release", "/usr/lib/os-release"];

impl std::fmt::Display for OSErrors {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

        let error_msg = match self {
            OSErrors::NoSuchFile => "Couldn't find os-release",
            OSErrors::FailedToRead => "Failed to read os-release",
            OSErrors::RegexError => "Regex failed"
        };

        write!(f, "{}: {:?}", "OS Info Err".bold().red(), error_msg)
    }
}

impl std::error::Error for OSErrors {}

/// Returns the operating system ID from an `os-release` file.
///
/// # Errors
///
/// Returns an [`OSErrors`] value if:
/// - No supported `os-release` file could be found
/// - The file could not be read or parsed
///
/// # Examples
///
/// ```rust
/// use fxkit::core::os::unix::linux::os_info::get_os;
///
/// match get_os() {
///     Ok(os) => println!("OS ID: {}", os),
///     Err(e) => eprintln!("Failed to detect OS: {}", e),
/// }
/// ```
pub fn get_os() -> Result<String, OSErrors> {
    let os_release = match find_os_release() {
        Ok(os) => os,
        Err(_) => return Err(OSErrors::NoSuchFile),
    };

    match read_id_os_release(&os_release) {
        Ok(os_value) => Ok(os_value),
        Err(_) => return Err(OSErrors::FailedToRead),
    }
}

/// Returns the operating system version from an `os-release` file.
///
/// # Errors
///
/// Returns an [`OSErrors`] value if:
/// - No supported `os-release` file could be found
/// - The file could not be read or parsed
///
/// # Examples
///
/// ```rust
/// use fxkit::core::os::unix::linux::os_info::get_os_version;
///
/// match get_os_version() {
///     Ok(os) => println!("OS Version: {}", os),
///     Err(e) => eprintln!("Failed to detect OS Version: {}", e),
/// }
/// ```
pub fn get_os_version() -> Result<String, OSErrors> {
    let os_release_path = match find_os_release() {
        Ok(path) => path,
        Err(_) => return Err(OSErrors::NoSuchFile),
    };

    let content = match std::fs::read_to_string(os_release_path) {
        Ok(os_content) => os_content,
        Err(_) => return Err(OSErrors::FailedToRead),
    };
    let reg = Regex::new(r#"(?m)^VERSION_ID=(?:(?:"(.*?)")|(?:(.*)))$"#)
        .map_err(|_| OSErrors::RegexError)?;

    let captures = reg.captures(&content).ok_or(OSErrors::RegexError)?;

    captures
        .get(2)
        .or(captures.get(1))
        .map(|m| m.as_str().to_string()).ok_or(OSErrors::FailedToRead)

}

/// Looks for a valid `os-release` file
///
/// # Errors
///
/// Returns an [`OSErrors`] value if:
/// - No supported `os-release` file could be found
///
/// # Examples
///
/// ```rust
/// use fxkit::core::os::unix::linux::os_info::find_os_release;
///
/// match find_os_release() {
///     Ok(os_release) => println!("OS release path: {}", os_release),
///     Err(e) => eprintln!("Failed to find OS release: {}", e),
/// }
/// ```
pub fn find_os_release() -> Result<String, OSErrors> {
    for os_release in OS_RELEASE_PATHS {
        if Path::new(os_release).exists() {
            return Ok(os_release.to_string())
        }
    }

    Err(OSErrors::NoSuchFile)
}

fn read_id_os_release(path: &str) -> Result<String, OSErrors> {
    let content = match std::fs::read_to_string(path) {
        Ok(os_content) => os_content,
        Err(_) => return Err(OSErrors::FailedToRead),
    };
    let re = Regex::new(r#"(?m)^ID=(?:(?:"(.*?)")|(?:(.*)))$"#).map_err(|_| OSErrors::RegexError)?;
    let captures = re.captures(&content).ok_or(OSErrors::RegexError)?;

    captures
        .get(2)
        .or(captures.get(1))
        .map(|m| m.as_str().to_string()).ok_or(OSErrors::RegexError)
}