adof 0.11.0

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

use anyhow::{Context, Result};
use sha2::{Digest, Sha256};

use crate::database::{get_database_path, get_table_struct};

pub mod add;
pub mod auto_update;
pub mod deploy;
pub mod init;
pub mod link;
pub mod list;
pub mod log;
pub mod patterns;
pub mod push;
pub mod readme;
pub mod remove;
pub mod summary;
pub mod uninstall;
pub mod unlink;
pub mod update;

fn select_files(found_files: Vec<PathBuf>) -> Result<Vec<String>> {
    let found_files = found_files
        .iter()
        .map(|file| {
            file.clone()
                .into_os_string()
                .into_string()
                .map_err(|os_str| {
                    anyhow::anyhow!("Failed to convert OsString to String: {:?}", os_str)
                })
        })
        .collect::<Result<Vec<String>>>()?
        .join("\n");

    let mut child = Command::new("fzf")
        .arg("--preview")
        .arg("cat {}")
        .arg("-m")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .context("Failed to start fzf")?;

    if let Some(mut stdin) = child.stdin.take() {
        stdin
            .write_all(found_files.as_bytes())
            .context("Failed to write to fzf stdin")?;
    }

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

    let selected_files = String::from_utf8_lossy(&output.stdout)
        .trim()
        .lines()
        .map(|file| file.to_string())
        .collect::<Vec<String>>();

    if selected_files.is_empty() {
        println!("No file selected.");
    }

    Ok(selected_files)
}

pub fn create_file(original_file: &str) -> Result<String> {
    let home_dir = adof::get_home_dir();
    let adof_dir = adof::get_adof_dir();

    let backup_file = original_file.replace(&home_dir, &adof_dir);

    let path = path::Path::new(&backup_file);
    let path_dir = path.parent().context("Failed to get parent directory")?;

    fs::create_dir_all(path_dir).context("Failed to create backup directory")?;
    fs::File::create(&backup_file).context("Failed to create backup file")?;

    Ok(backup_file)
}

fn calculate_file_hash(file_path: &str) -> Result<Vec<u8>> {
    let mut file = fs::File::open(file_path).context(format!(
        "Failed to open file for hash calculation: {}",
        file_path
    ))?;
    let mut hasher = Sha256::new();
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer)
        .context("Failed to read file into buffer")?;
    hasher.update(&buffer);
    Ok(hasher.finalize().to_vec())
}

fn is_file_backedup(original_file: &str) -> Result<bool> {
    let table_struct = get_table_struct()?;
    Ok(table_struct.table.contains_key(original_file))
}

fn check_for_init() -> Result<bool> {
    let database_path = get_database_path();
    fs::exists(&database_path).context("Failed to check if the adof is already initialized")
}

fn get_pid_file() -> String {
    let adof_dir = adof::get_adof_dir();
    format!("{}/do_not_touch/pid.txt", adof_dir)
}