unin-bin 0.1.1

A universal installer for many languages so you don't have to remember any command
Documentation
//unPack ver 0.0.1

use colored::Colorize;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fmt::{Debug, Display};
use std::fs::{self, OpenOptions, create_dir_all};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::exit;
use time::{OffsetDateTime, PrimitiveDateTime};

#[derive(Serialize, Deserialize, PartialEq, Clone)]
pub struct UninPackage {
    pub name: String,
    pub paths: Vec<PathBuf>,
    pub change_date: String,
    pub updated: bool,
}

impl Display for UninPackage {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "\tName: {}\n\tPaths: {:?}\n\tChange Date: {}\n",
            self.name, self.paths, self.change_date
        )
    }
}
impl UninPackage {
    pub fn new(name: String) -> Self {
        UninPackage {
            name,
            paths: Vec::new(),
            change_date: "1970-01-01 00:00:00.0".to_string(),
            updated: false,
        }
    }
}
pub struct DebuggableOptionUninPackage(pub Option<UninPackage>);

impl Debug for DebuggableOptionUninPackage {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self.0 {
            Some(p) => write!(f, "{}", p),
            None => write!(f, "None"),
        }
    }
}

pub fn get_registry() {
    if !registry_exists() {
        println!("{}", "Registry couldn't be found nor created. Consider creating it manually at ~/.unin/registry/registry.json".red());
    }
    let home = std::env::var("HOME").unwrap();
    let registry_path = PathBuf::from(format!("{}/.unin/registry/", home));
    let registry_file = PathBuf::from(format!("{}/registry.json", registry_path.to_str().unwrap()));
    let data = fs::read_to_string(registry_file).unwrap();
    let packages: Vec<UninPackage> =
        serde_json::from_str(&data).unwrap_or_else(|e| panic!("Shit happened: {:?}", e));
    for p in packages {
        println!("\n{}", p);
    }
}
pub fn time_create() -> String {
    let now_utc = OffsetDateTime::now_utc();
    let primitive_now_utc = PrimitiveDateTime::new(
        now_utc.date(),
        now_utc
            .time()
            .truncate_to_second()
            .replace_nanosecond(0)
            .unwrap(),
    );
    let fucking_string = PrimitiveDateTime::to_string(&primitive_now_utc);
    format!("{}", fucking_string)
}

pub fn time_read() -> PrimitiveDateTime {
    let registry_path = PathBuf::from(format!(
        "{}/.unin/registry/",
        String::from(std::env::var("HOME").unwrap_or_else(|_| "/root/".to_string()))
    ));
    let registry_file = PathBuf::from(format!("{}/registry.json", registry_path.to_str().unwrap()));
    let data = std::fs::read_to_string(registry_file).unwrap();
    let p: UninPackage =
        serde_json::from_str(&data).unwrap_or_else(|e| panic!("Shit happened: {:?}", e));
    let time_string: String = p.change_date.clone();
    let x: PrimitiveDateTime =
        PrimitiveDateTime::parse(&time_string, &time::format_description::well_known::Rfc3339)
            .unwrap();
    println!("{}", String::from(x.to_string()));
    x
}

pub fn registry_write(package: &UninPackage) {
    let registry_path = format!(
        "{}/.unin/registry/registry.json",
        std::env::var("HOME").unwrap()
    );
    let registry_dir = Path::new(&registry_path).parent().unwrap();

    let _ = create_dir_all(registry_dir);

    let existing_content = fs::read_to_string(&registry_path).unwrap_or_else(|_| String::new());
    let mut is_new = true;
    for line in existing_content.lines() {
        if line.contains(package.name.trim()) {
            is_new = false;
            break;
        }
    }

    let mut packages: Vec<UninPackage> =
        serde_json::from_str(&existing_content).unwrap_or_else(|_| Vec::new());

    let package_name = package.name.clone();

    if let Some(pos) = packages.iter().position(|p| p.name == package.name) {
        packages[pos].updated = true;
        packages[pos].change_date = time_create();
        packages[pos].paths = package.paths.clone();
    } else {
        packages.push((*package).clone())
    }

    let mut file = OpenOptions::new()
        .write(true)
        .create(true)
        .truncate(true)
        .open(&registry_path)
        .unwrap();

    serde_json::to_writer(&mut file, &packages).unwrap();
    file.flush().unwrap();

    if is_new {
        println!(
            "Registry for entry {} created successfully!",
            package_name.green()
        );
    } else {
        println!(
            "Registry for entry {} updated successfully!",
            package_name.green()
        );
    }
}
pub fn registry_get_package(package_name: String) -> Option<UninPackage> {
    if !registry_exists() {
        println!("{}", "Registry couldn't be found nor created. Consider creating it manually at ~/.unin/registry/registry.json".red());
    }
    let home = std::env::var("HOME").unwrap();
    let registry_path = PathBuf::from(format!("{}/.unin/registry/", home));
    let registry_file = PathBuf::from(format!("{}/registry.json", registry_path.to_str().unwrap()));
    let data = fs::read_to_string(registry_file).unwrap();
    let packages: Vec<UninPackage> =
        serde_json::from_str(&data).unwrap_or_else(|e| panic!("Shit happened: {:?}", e));

    packages.into_iter().find(|p| p.name == package_name)
}
pub fn registry_exists() -> bool {
    let registry_path = PathBuf::from(format!(
        "{}/.unin/registry/",
        std::env::var("HOME").unwrap()
    ));
    let registry_file = PathBuf::from(format!("{}/registry.json", registry_path.to_str().unwrap()));
    if !registry_file.exists() {
        if !create_dir_all(&registry_path).is_ok() {
            // Create the DIRECTORY, not the file path
            println!("Failed to create registry for unPack ver 0.0.1");
            false //the directory didn't get created, so it doesn't exist.
        } else {
            // Create the empty registry file
            if let Ok(mut file) = std::fs::File::create(&registry_file) {
                let _ = file.write_all(b"[]"); // Write empty JSON array
                true //it exists now
            } else {
                println!("Failed to create registry file");
                false //It didn't get created, so it doesn't exist.
            }
        }
    } else {
        true //It existed and it will exist forever.
    }
}
pub fn registry_uninstall(package_name: String) {
    let home = std::env::var("HOME").unwrap();
    let registry_path: PathBuf = PathBuf::from(format!("{}/.unin/registry/registry.json", home));
    let registry_json: Value =
        serde_json::from_str(&fs::read_to_string(registry_path.clone()).unwrap()).unwrap();
    let mut packages: Vec<UninPackage> = serde_json::from_value(registry_json).unwrap();
    let package_remove_queued = packages
        .clone()
        .into_iter()
        .find(|p| p.name == package_name);
    println!("\n{}", &package_remove_queued.clone().unwrap());

    let confirmation = dialoguer::Confirm::new()
        .with_prompt("Are you sure you want to delete this application?")
        .interact()
        .unwrap();
    if !confirmation {
        exit(0)
    }

    let delete_job = std::process::Command::new("sudo")
        .arg("rm".to_string())
        .arg("-f")
        .arg(
            &package_remove_queued.clone().unwrap().paths[0]
                .to_str()
                .unwrap(),
        )
        .output()
        .unwrap()
        .status;

    if !delete_job.success() {
        println!(
            "Failed to delete the file for {}",
            package_remove_queued.clone().unwrap().name
        );
        let confirmation = dialoguer::Confirm::new()
            .with_prompt("Do you want to delete the registry entry anyway?")
            .interact()
            .unwrap();
        if confirmation {
            packages.retain(|p| p.name != package_name);
            let _ = std::fs::write(registry_path, serde_json::to_string(&packages).unwrap());
            println!("Registry entry for {} deleted", package_name);
            exit(0)
        } else {
            println!("Aborting");
        }
    } else {
        packages.retain(|p| p.name != package_name);
        let _ = fs::write(registry_path, serde_json::to_string(&packages).unwrap());
        println!("Registry entry for {} deleted", package_name);
    }
}

pub fn temp_test() {
    let x: UninPackage = UninPackage {
        name: String::from("dev"),
        paths: vec![PathBuf::from("/root/ad"), PathBuf::from("/root/da")],
        change_date: String::from(time_create()),
        updated: false,
    };
    let _lol = get_registry();
}
pub fn return_registry_path() -> PathBuf {
    let registry_path = PathBuf::from(format!(
        "{}/.unin/registry/registry.json",
        std::env::var("HOME").unwrap()
    ));
    registry_path
}