unin-bin 0.1.3

A universal installer for many languages so you don't have to remember any commands
Documentation
use crate::logging::log_to_file;
use crate::tools::find_files_because_the_user_is_too_lazy;
use colored::Colorize;
use rand::RngExt;
use std::fs;
use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf;
use std::process::{Command, Stdio, exit};
use duct::cmd;
use unin_bin::{UninPackage, registry_write, time_create};

pub fn start_meson(directory: PathBuf, noinstall: bool) {
    let setup = cmd!("meson", "setup", "build")
        .stderr_to_stdout()
        .dir(directory.clone())
        .unchecked();

    let stdout = setup.reader().unwrap();
    let reader = BufReader::new(stdout);
    let mut full_content = String::new();
    let mut has_error = false;
    for line in reader.lines().map_while(Result::ok) {
        let raw_content = line.clone();
        let shc = line.clone();
        if shc.contains("ERROR") {
            has_error = true;
            print!("\r\x1B[K{}", shc.red().underline().bold());
            std::io::stdout().flush().unwrap(); //FLUSH THE STDOUT
            full_content.push_str(format!("{}\n", &raw_content.clone()).as_str());
            continue;
        }
        print!("\r\x1B[K{}", line.purple().bold());
        std::io::stdout().flush().unwrap();
        let mut rng = rand::rng();
        let random_delay: u64 = rng.random_range(30..=60);
        std::thread::sleep(std::time::Duration::from_millis(random_delay));
        full_content.push_str(format!("{}\n", &raw_content.clone()).as_str());
    }

    if has_error {
        println!(
            "{} The full error will be printed here.\n",
            "\nAn error occurred while configuring the project."
                .red()
                .bold()
        );
        println!(
            "{}",
            full_content.trim_end().replace("error:", &"error:".red())
        );
        exit(1);
    }
    let write_log = log_to_file(directory.clone(), "meson".to_string(), full_content);
    println!(
        "\nLog for the unin install step \"meson\" can be found here: {}",
        write_log
    );
    drop(write_log);

    let cpu_cores = num_cpus::get();

    let cmd = cmd!("meson", "compile", "-C", "build", "-j", &cpu_cores.to_string())
        .dir(directory.clone())
        .stderr_to_stdout()
        .unchecked();

    let stdout = cmd.reader().unwrap();
    let reader = BufReader::new(stdout);
    let mut content_full = String::new();
    let mut has_error = false;
    for line in reader.lines().map_while(Result::ok) {
        print!("\r\x1B[K{}", line.purple().bold());
        std::io::stdout().flush().unwrap();
        if line.contains("error:") {
            has_error = true;
            content_full.push_str(line.as_str());
            continue;
        }
        content_full.push_str(line.as_str());
        continue;
    }
    if has_error {
        println!(
            "Build using ninja failed. If you want, I can show you the output of the build process. I don't care if you want to, here it is:"
        );
        content_full
            .split("\n")
            .for_each(|line| println!("{}", line));
    }
    let log = log_to_file(directory.clone(), "ninja".to_string(), content_full);
    println!(
        "\nLog for unin step \"ninja\" build can be found here: {}",
        log
    );
    drop(log);

    if noinstall {
        println!("Build finished successfully.");
        println!("I have no idea where the binaries are. They are somewhere, go find them")
    }
    let file_path: String = String::from(
        format!(
            "{}/build/meson-private/install.dat",
            directory.to_str().unwrap()
        )
        .as_str(),
    );
    let exists = fs::metadata(file_path).is_ok();
    if !exists {
        println!(
            "\nThe project does not provide an \"install\" rule. This means that I cannot install the binaries. You can still find them somewhere in build/"
        );
        exit(1);
    }

    let install = cmd!("ninja", "-C", "build", "install")
        .stderr_to_stdout()
        .dir(directory.clone())
        .unchecked();

    let stdout = install.reader().unwrap();
    let reader = BufReader::new(stdout);
    let mut full_content = String::new();
    let mut has_error = false;
    for line in reader.lines().map_while(Result::ok) {
        let coc = line.clone();
        if line.contains("error:") || line.contains("fatal error") || line.contains("failed") {
            full_content.push_str(format!("{}\n", &coc).as_str());
            has_error = true
        } else {
            print!("\r\x1B[K{}", line.purple().bold());
            std::io::stdout().flush().unwrap();
            std::thread::sleep(std::time::Duration::from_millis(10));
            full_content.push_str(format!("{}\n", &coc).as_str());
        }
    }

    let log = log_to_file(
        directory.clone(),
        "install".to_string(),
        full_content.clone(),
    );
    println!("\nLog for unin step \"install\" can be found here: {}", log);
    drop(log);

    if has_error {
        println!("Installation failed. Here is the full output:");
        println!("{}", full_content);
        exit(1);
    }
    println!("Installation finished successfully.");
    let output_dir = format!("{}/build/", directory.to_str().unwrap());
    let files = find_files_because_the_user_is_too_lazy(PathBuf::from(output_dir.clone()));
    for binary in files {
        let last_item_binary = binary
            .to_str()
            .unwrap()
            .split("/")
            .collect::<Vec<&str>>()
            .last()
            .unwrap()
            .to_string();
        let installed_absolute_path = format!("/usr/local/bin/{}", last_item_binary);
        let temp_binary: UninPackage = UninPackage {
            name: binary
                .to_str()
                .unwrap()
                .split('/')
                .collect::<Vec<&str>>()
                .last()
                .unwrap()
                .to_string(),
            paths: vec![PathBuf::from(installed_absolute_path)],
            change_date: time_create(),
            updated: false,
        };
        registry_write(&temp_binary, true);
        println!("\n{}", temp_binary);
    }
}
pub fn clean(directory: PathBuf) {
    if !directory.exists() {
        println!("Nothing to do.");
        exit(0)
    }
    let cleaner = fs::remove_dir_all(format!("{}/build", directory.to_str().unwrap()));
    if cleaner.is_err() {
        println!("Cleaning failed.");
        exit(1);
    }
    println!("Cleaned build directory.");
}