adof 0.10.0

ADOF - An Automatic Dot-files Organizer Friend
Documentation
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use std::process::{self, Command, Stdio};
use std::sync::mpsc::{self, Receiver};
use std::thread;

use glob::glob;

use adof::get_home_dir;

use crate::database::add::add_files;
use crate::git::add::git_add;
use crate::init::init;

use super::*;

pub async fn add() {
    if !check_for_init() {
        init().await;
        process::exit(1);
    }

    let files_to_add = get_files_to_add();

    if files_to_add.is_empty() {
        println!("files are already exist");
        process::exit(1);
    }

    create_backup_files(&files_to_add);
    git_add();
}

fn get_files_to_add() -> Vec<String> {
    let home_dir = get_home_dir();
    let pattern = format!("{}/**/*", home_dir);

    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        for entry in glob(&pattern).expect("Failed to read glob pattern") {
            let path = entry.unwrap();
            if path.is_file() {
                tx.send(path).expect("Failed to send file path");
            }
        }
    });

    let selected_files = select_files(rx);

    selected_files
        .into_iter()
        .filter(|file| !is_file_backedup(file))
        .collect::<Vec<String>>()
}

fn select_files(rx: Receiver<PathBuf>) -> Vec<String> {
    let mut child = Command::new("fzf")
        .arg("--preview")
        .arg("cat {}")
        .arg("-m")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .expect("Failed to start fzf");

    let mut stdin = child.stdin.take().expect("Failed to open fzf stdin");

    thread::spawn(move || {
        for path in rx.iter() {
            if let Some(file_path) = path.to_str() {
                writeln!(stdin, "{}", file_path).expect("Failed to write to fzf stdin");
            }
        }
    });

    let output = child.wait_with_output().expect("Failed to read fzf output");

    String::from_utf8_lossy(&output.stdout)
        .trim()
        .lines()
        .map(|file| file.to_string())
        .collect::<Vec<String>>()
}

fn create_backup_files(files_to_add: &[String]) {
    (0..files_to_add.len()).for_each(|i| {
        let backup_file = create_file(&files_to_add[i]);
        fs::copy(&files_to_add[i], &backup_file).unwrap();
        add_files(&files_to_add[i], &backup_file);
    })
}