mrvillage-cli 0.1.15

My personal CLI
use std::path::{Path, PathBuf};

use include_dir::{Dir, File};
use tempfile::tempdir;
use xshell::Shell;

use crate::{quiet_cmd, utils::template::flatten_dir};

use super::template_file::TemplateFileWrapper;

#[derive(Debug)]
pub struct Template {
    pub name: &'static str,
    pub dir: Dir<'static>,
    pub config_files: &'static [TemplateFileWrapper],
    pub git: bool,
}

impl Template {
    pub fn write(
        &self,
        dir: PathBuf,
        interpolator: impl Fn(&str) -> String,
        git: Option<bool>,
    ) -> anyhow::Result<()> {
        println!("Creating {} project in {}", self.name, dir.display());
        let git = git.unwrap_or(self.git);
        if dir.exists() {
            return Err(anyhow::anyhow!("That directory already exists!"));
        }
        let temp_dir = tempdir()?;
        let res = self.write_files(temp_dir.path(), &interpolator, git);
        if let Err(e) = res {
            temp_dir.close()?;
            Err(e)
        } else {
            std::fs::rename(temp_dir.path(), dir)?;
            std::fs::create_dir(temp_dir.path())?;
            temp_dir.close()?;
            Ok(())
        }
    }

    fn write_files(
        &self,
        dir_path: &Path,
        interpolator: &impl Fn(&str) -> String,
        git: bool,
    ) -> anyhow::Result<()> {
        let dirs = flatten_dir(&self.dir);
        for i in dirs.iter() {
            std::fs::create_dir_all(dir_path.join(i.path()))?;
        }
        let files: Vec<&File> = dirs.into_iter().flat_map(|i| i.files()).collect();
        for i in files {
            let path = dir_path.join(i.path());
            if let Some(contents) = i.contents_utf8() {
                let contents = interpolator(contents);
                std::fs::write(path, contents)?;
            } else {
                std::fs::write(path, i.contents())?;
            }
        }
        for i in self.config_files.iter() {
            i.file.write(&dir_path.join(i.dir), interpolator)?;
        }
        if git {
            let sh = Shell::new()?;
            sh.change_dir(dir_path);
            quiet_cmd!(sh, "git init").run()?;
        }
        Ok(())
    }
}