tinty 0.23.0

Change the theme of your terminal, text editor and anything else with one command!
use anyhow::{anyhow, Result};
use std::error::Error;
use std::fs::{self, File};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str;

#[allow(dead_code)]
pub const REPO_NAME: &str = env!("CARGO_PKG_NAME");
#[allow(dead_code)]
pub const ORG_NAME: &str = "tinted-theming";
pub const COMMAND_NAME: &str = "./target/release/tinty";
#[allow(dead_code)]
pub const CURRENT_SCHEME_FILE_NAME: &str = "current_scheme";
#[allow(dead_code)]
pub const REPO_DIR: &str = "repos";
#[allow(dead_code)]
pub const SCHEMES_REPO_NAME: &str = "schemes";
#[allow(dead_code)]
pub const CUSTOM_SCHEMES_DIR_NAME: &str = "custom-schemes";

pub fn run_command(command_vec: Vec<String>) -> Result<(String, String), Box<dyn Error>> {
    let output = Command::new(&command_vec[0])
        .args(&command_vec[1..])
        .output()
        .expect("Failed to execute command");

    if !output.stderr.is_empty() {
        println!(
            "tests::utils::run_command stderr: {}",
            String::from_utf8_lossy(&output.stderr)
        );
    }

    let stdout = strip_ansi_escapes::strip(String::from_utf8(output.stdout)?);
    let stderr = strip_ansi_escapes::strip(String::from_utf8(output.stderr)?);

    Ok((String::from_utf8(stdout)?, String::from_utf8(stderr)?))
}

#[allow(dead_code)]
pub fn run_install_command(config_path: &Path, data_path: &Path) -> Result<()> {
    let output_install = Command::new(COMMAND_NAME)
        .args([
            "install",
            format!("--config={}", config_path.display()).as_str(),
            format!("--data-dir={}", data_path.display()).as_str(),
        ])
        .status()
        .expect("Failed to execute install command");

    if output_install.success() {
        Ok(())
    } else {
        Err(anyhow!("Install command stderr: {}", output_install))
    }
}

pub fn cleanup(config_path: impl AsRef<Path>, data_path: impl AsRef<Path>) -> Result<()> {
    if config_path.as_ref().is_file() {
        fs::remove_file(config_path)?;
    }

    if data_path.as_ref().is_dir() {
        fs::remove_dir_all(data_path)?;
    }

    Ok(())
}

pub fn write_to_file(path: impl AsRef<Path>, contents: &str) -> Result<()> {
    if path.as_ref().exists() {
        fs::remove_file(&path)?;
    }

    if path.as_ref().parent().is_some() && !path.as_ref().parent().unwrap().exists() {
        fs::create_dir_all(path.as_ref().parent().unwrap())?;
    }

    let mut file = File::create(path)?;

    file.write_all(contents.as_bytes())?;

    Ok(())
}

#[allow(clippy::type_complexity)]
pub fn setup(
    name: &str,
    command: &str,
) -> Result<(
    PathBuf,
    PathBuf,
    Vec<String>,
    Box<dyn FnOnce() -> Result<()>>,
)> {
    let config_path = PathBuf::from(format!("config_path_{}.toml", name).as_str());
    let data_path = PathBuf::from(format!("data_path_{}", name).as_str());
    let command = format!(
        "{} --config=\"{}\" --data-dir=\"{}\" {}",
        COMMAND_NAME,
        config_path.display(),
        data_path.display(),
        command
    );
    let command_vec = shell_words::split(command.as_str()).map_err(anyhow::Error::new)?;

    cleanup(&config_path, &data_path)?;
    write_to_file(&config_path, "")?;

    let config_path_clone = config_path.clone();
    let data_path_clone = data_path.clone();

    Ok((
        config_path,
        data_path,
        command_vec,
        Box::new(move || cleanup(&config_path_clone, &data_path_clone)),
    ))
}

#[allow(dead_code)]
pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
    fs::create_dir_all(&dst)?;

    for entry in fs::read_dir(src)? {
        let entry = entry?;
        let file_type = entry.file_type()?;
        let dest_path = dst.as_ref().join(entry.file_name());

        if file_type.is_dir() {
            copy_dir_all(entry.path(), &dest_path)?;
        } else {
            fs::copy(entry.path(), &dest_path)?;
        }
    }
    Ok(())
}