xbp 0.4.1

XBP is a build pack and deployment management tool to deploy, rust, nextjs etc and manage the NGINX configs below it
Documentation
//! Package installation module for xbp
//!
//! This module provides functionality to install various packages using
//! predefined shell scripts. It supports executing installation scripts
//! for supported packages and provides debug output for troubleshooting.

use std::path::{Path, PathBuf};
use std::process::Output;
use tokio::process::Command;

/// Installs a package by name using the corresponding installation script
///
/// # Arguments
///
/// * `package_name` - The name of the package to install (e.g., "grafana")
/// * `debug` - Whether to enable debug output during installation
///
/// # Returns
///
/// * `Ok(())` if the installation was successful
/// * `Err(String)` if the package is not supported or installation failed
///
/// # Examples
///
/// ```
/// let result = install_package("grafana", true).await;
/// match result {
///     Ok(()) => println!("Installation successful"),
///     Err(e) => eprintln!("Installation failed: {}", e),
/// }
/// ```
pub async fn install_package(package_name: &str, debug: bool) -> Result<(), String> {
    // Router function to match package names
    match package_name.to_lowercase().as_str() {
        "grafana" => execute_install_script("grafana", debug).await,
        "scylladb" => execute_install_script("scylladb", debug).await,
        "nginx_full" => execute_install_script("nginx_full", debug).await,
        "opencv-rust" => execute_install_script("opencv-rust", debug).await,
        "elixir_erlang" => execute_install_script("elixir_erlang", debug).await,
        "docker" => execute_install_script("docker", debug).await,
        "triggerdotdev" => execute_install_script("triggerdotdev", debug).await,
        "iotop" => execute_install_script("iotop", debug).await,
        _ => Err(format!(
            "Package '{}' is not supported. Available packages: grafana, scylladb, nginx_full, opencv-rust, docker, elixir_erlang, triggerdotdev, iotop",
            package_name
        )),
    }
}

/// Executes the installation script for a specific package
///
/// This function locates the installation script in the package_install_scripts
/// directory, makes it executable, and runs it. It provides detailed debug
/// output when debug mode is enabled.
///
/// # Arguments
///
/// * `package_name` - The name of the package (used to find the script file)
/// * `debug` - Whether to enable debug output during script execution
///
/// # Returns
///
/// * `Ok(())` if the script executed successfully
/// * `Err(String)` if the script was not found, couldn't be made executable, or failed to run
///
/// # Script Location
///
/// Scripts are expected to be located at `./package_install_scripts/{package_name}.sh`
async fn execute_install_script(package_name: &str, debug: bool) -> Result<(), String> {
    let script_path = format!("./package_install_scripts/{}.sh", package_name);
    let script_pathbuf = PathBuf::from(&script_path);

    if debug {
        println!("[DEBUG] Looking for install script at: {}", script_path);
    }

    // Check if the script exists
    if !script_pathbuf.exists() {
        return Err(format!("Install script not found: {}", script_path));
    }

    if debug {
        println!("[DEBUG] Making script executable: {}", script_path);
    }

    // Make the script executable
    let chmod_output: Output = Command::new("chmod")
        .arg("+x")
        .arg(&script_path)
        .output()
        .await
        .map_err(|e| format!("Failed to execute chmod command: {}", e))?;

    if !chmod_output.status.success() {
        return Err(format!(
            "Failed to make script executable: {}",
            String::from_utf8_lossy(&chmod_output.stderr)
        ));
    }

    if debug {
        println!("[DEBUG] Executing install script: {}", script_path);
    }

    // Execute the install script
    let script_output: Output = Command::new("bash")
        .arg(&script_path)
        .output()
        .await
        .map_err(|e| format!("Failed to execute install script: {}", e))?;

    if debug {
        println!("[DEBUG] Script output: {:?}", script_output);
    }

    if !script_output.status.success() {
        return Err(format!(
            "Install script failed: {}",
            String::from_utf8_lossy(&script_output.stderr)
        ));
    }

    // Print the script output
    let stdout = String::from_utf8_lossy(&script_output.stdout);
    if !stdout.trim().is_empty() {
        println!("{}", stdout);
    }

    println!(
        "\x1b[92m{} installation completed successfully!\x1b[0m",
        package_name
    );

    Ok(())
}