fxkit 0.1.3

Useful utilities for writting Rust CLI tools
Documentation
use crate::core::tools::process_running::linux::is_process_running;
use colored::Colorize;
use regex::Regex;
use std::fs::{read_dir, read_link};
use std::path::Path;

#[derive(Debug)]
pub enum BedrockErrors {
    NotOnBedrock,
    NoStratas,
    ReadError,
    RegexError,
    InvalidPmmInterface,
    StrataAlreadyExists,
    InvalidStrata,
}

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

        let error_msg = match self {
            BedrockErrors::NotOnBedrock => "Not on Bedrock",
            BedrockErrors::NoStratas => "No stratas could be found",
            BedrockErrors::ReadError => "Failed to read file",
            BedrockErrors::RegexError => "Regex has failed",
            BedrockErrors::InvalidPmmInterface => "Invalid interface has been set for PMM.",
            BedrockErrors::StrataAlreadyExists => "Strata with such name already exist.",
            BedrockErrors::InvalidStrata => "Couldn't get such strata."
        };

        write!(f, "{}: {:?}", "BedrockErr".bold().red(), error_msg)
    }
}

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

/// Check if Bedrock Linux is present
///
/// # Example
/// ```rust
/// use fxkit::core::os::unix::linux::bedrock::check_bedrock;
/// if check_bedrock() {
///     // Code
/// }
/// ```
///
pub fn check_bedrock() -> bool {
    Path::new("/bedrock").exists() && is_process_running("etcfs") && is_process_running("crossfs")
}

/// Returns the amount of strata's on a Bedrock system
///
/// # Errors
///
/// Returns an [`BedrockErrors`] value if:
/// - Not on Bedrock
/// - No stratas could be found
///
/// # Examples
///
/// ```rust
/// use fxkit::core::os::unix::linux::bedrock::list_strata;
///
/// match list_strata() {
///     Ok(strata) => println!("Strata count: {}", strata),
///     Err(e) => eprintln!("Failed to count stratas: {}", e),
/// }
/// ```
pub fn list_strata() -> Result<usize, BedrockErrors> {
    if !check_bedrock() {
        return Err(BedrockErrors::NotOnBedrock);
    }

    let bedrock_strata_folder = "/bedrock/strata";

    if let Ok(strata_count) = read_dir(bedrock_strata_folder) {
        Ok(strata_count.count() - 3)
    } else {
        Err(BedrockErrors::NoStratas)
    }
}

/// Returns the amount of strata's on a Bedrock system
///
/// # Errors
///
/// Returns an [`BedrockErrors`] value if:
/// - Not on Bedrock
/// - No stratas could be found
///
/// # Examples
///
/// ```rust
/// use fxkit::core::os::unix::linux::bedrock::running_in_strat;
///
/// match running_in_strat() {
///     Ok(running) => {
///        if running {
///             println!("Yes")      
///        }
///     },
///     Err(e) => eprintln!("Failed to detect if running in strat: {}", e),
/// }
/// ```
pub fn running_in_strat() -> Result<bool, BedrockErrors> {
    if !check_bedrock() {
        return Err(BedrockErrors::NotOnBedrock);
    }

    let path = std::env::var("PATH");

    match path {
        Ok(path) => {
            if !path.contains("/bedrock/cross/bin") {
                return Ok(true);
            }
        }

        Err(_) => return Err(BedrockErrors::ReadError),
    }

    Ok(false)
}

/// Detect what package manager is pmm mimicking
///
/// # Errors
///
/// Returns an [`BedrockErrors`] value if:
/// - Not on Bedrock
/// - Couldn't read ``/bedrock/etc/bedrock.conf``
/// - Regex failed to match anything
///
/// # Examples
///
/// ```rust
/// use fxkit::core::os::unix::linux::bedrock::detect_pmm_interface;
///
/// match detect_pmm_interface() {
///     Ok(pmm) => println!("Pmm interface: {}", pmm),
///     Err(e) => eprintln!("Failed to detect pmm's interface: {}", e),
/// }
/// ```
pub fn detect_pmm_interface() -> Result<String, BedrockErrors> {
    if !check_bedrock() {
        return Err(BedrockErrors::NotOnBedrock);
    }

    let bedrock_conf = "/bedrock/etc/bedrock.conf";

    let content = std::fs::read_to_string(bedrock_conf).map_err(|_| BedrockErrors::ReadError)?;
    let re = Regex::new(r#"(?m)^user-interface = (?:(?:"(.*?)")|(?:(.*)))$"#)
        .map_err(|_| BedrockErrors::RegexError)?;
    let captures = re.captures(&content).ok_or(BedrockErrors::RegexError)?;

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

/// Detect what stratum is providing the init
///
/// ## Example
/// ```rust
/// use fxkit::core::os::unix::linux::bedrock::detect_stratum_init;
/// println!("Strata: {}", detect_stratum_init());
/// ```
///
pub fn detect_stratum_init() -> Result<String, BedrockErrors> {
    if !check_bedrock() {
        return Err(BedrockErrors::NotOnBedrock);
    }

    let init_stratum_path =
        read_link(Path::new("/bedrock/run/init-alias")).map_err(|_| BedrockErrors::ReadError)?;

    let init_stratum = init_stratum_path
        .to_string_lossy()
        .to_string()
        .replace("/bedrock/strata/", "");

    Ok(init_stratum)
}