fxkit 0.1.4

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

use regex::Regex;

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

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",
            OSErrors::NoSuchField => "No such field exists in the file",
        };

        write!(f, "{}", 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> {
    read_value_os_release("NAME")
}

pub fn get_os_id() -> Result<String, OSErrors> {
    read_value_os_release("ID")
}

/// 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> {
    read_value_os_release("VERSION_ID")
}

/// 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)
}

pub fn read_value_os_release(value: &str) -> Result<String, OSErrors> {
    let os_release_path = find_os_release().map_err(|_| OSErrors::NoSuchFile)?;

    let content = std::fs::read_to_string(os_release_path).map_err(|_| OSErrors::FailedToRead)?;

    let reg_format = format!(r#"(?m)^{}=(?:"(.*?)"|(.+))$"#, regex::escape(value));

    let reg = Regex::new(&reg_format).map_err(|_| OSErrors::RegexError)?;

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

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