spawn-cli 0.9.0

A command-line tool for creating files and folders from a template.
use anyhow::{Error, Result};
use directories::ProjectDirs;
use log::info;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, path::PathBuf};

const IGNORE_GLOBAL_FILENAME: &str = ".spwnignore_global";

#[derive(Default, Deserialize, Serialize)]
pub struct Config {
    aliases: HashMap<String, String>,
}

impl Config {
    pub fn read() -> Result<Config> {
        let config_path = config_path()?;
        let Ok(config_data) = std::fs::read_to_string(&config_path) else {
            return Ok(Config::default());
        };

        info!("Using config file {}", config_path.display());

        let config: Config = toml::from_str(&config_data)?;

        Ok(config)
    }

    pub fn write(&self) -> Result<()> {
        let config_data = toml::to_string(self)?;
        let config_path = config_path()?;

        if let Some(parent) = config_path.parent() {
            std::fs::create_dir_all(parent)?;
        }

        info!("Writing config to {}", config_path.display());

        std::fs::write(config_path, config_data)?;

        Ok(())
    }

    pub fn resolve_alias(&self, uri: String) -> String {
        if let Some(uri) = self.aliases.get(&uri) {
            uri.clone()
        } else {
            uri
        }
    }

    pub fn get_aliases(&self) -> &HashMap<String, String> {
        &self.aliases
    }

    pub fn add_alias(&mut self, name: String, uri: String) -> &Self {
        self.aliases.insert(name, uri);

        self
    }

    pub fn remove_alias(&mut self, name: &str) -> &Self {
        self.aliases.remove(name);

        self
    }
}

pub(crate) fn init() -> Result<()> {
    let Some(config_dir) = config_dir() else {
        return Err(Error::msg("No config directory"));
    };

    if config_dir.is_dir() {
        return Ok(());
    }

    let config_dir = config_dir.as_path();

    info!("Initializing config at {}", config_dir.display());

    std::fs::create_dir_all(config_dir)?;
    std::fs::write(
        config_dir.join(IGNORE_GLOBAL_FILENAME),
        include_str!("../.spwnignore_global"),
    )?;

    Ok(())
}

pub(crate) fn config_dir() -> Option<PathBuf> {
    ProjectDirs::from("com", "paulvandermeijs", "spwn")
        .map(|proj_dirs| proj_dirs.config_dir().to_path_buf())
}

pub(crate) fn cache_dir() -> Option<PathBuf> {
    ProjectDirs::from("com", "paulvandermeijs", "spwn")
        .map(|proj_dirs| proj_dirs.cache_dir().to_path_buf())
}

pub(crate) fn get_global_ignore() -> Result<Vec<String>> {
    let Some(mut global_ignore_file) = config_dir() else {
        return Err(Error::msg("No config directory"));
    };

    global_ignore_file.push(IGNORE_GLOBAL_FILENAME);

    if !global_ignore_file.is_file() {
        return Err(Error::msg("No global ignore file"));
    }

    info!(
        "Using global ignore file {:?}",
        global_ignore_file.display()
    );

    let lines = crate::fs::read_lines(global_ignore_file)?;

    Ok(lines)
}

fn config_path() -> Result<PathBuf> {
    let Some(mut config_path) = config_dir() else {
        return Err(Error::msg("No config directory"));
    };

    config_path.push("config.toml");

    Ok(config_path)
}