archetect-core 0.7.3

Generates Content from Archetype Template Directories and Git Repositories.
Documentation
use crate::config::CATALOG_FILE_NAME;
use directories::ProjectDirs;
use std::fmt::{Display, Error, Formatter};
use std::path::{Path, PathBuf};
use tempfile::tempdir;

pub enum LayoutType {
    Native,
    DotHome,
    Temp,
}

pub trait SystemLayout {
    fn configs_dir(&self) -> PathBuf;

    fn cache_dir(&self) -> PathBuf;

    fn catalog_cache_dir(&self) -> PathBuf {
        self.cache_dir().join("catalogs")
    }

    fn git_cache_dir(&self) -> PathBuf {
        self.cache_dir().join("git")
    }

    fn http_cache_dir(&self) -> PathBuf {
        self.cache_dir().join("http")
    }

    fn answers_config(&self) -> PathBuf {
        self.configs_dir().join("answers.yml")
    }

    fn catalog(&self) -> PathBuf {
        self.configs_dir().join(CATALOG_FILE_NAME)
    }
}

#[derive(Debug)]
pub struct NativeSystemLayout {
    project: ProjectDirs,
}

impl NativeSystemLayout {
    pub fn new() -> Result<NativeSystemLayout, SystemError> {
        match ProjectDirs::from("", "", "archetect") {
            Some(project) => Ok(NativeSystemLayout { project }),
            None => Err(SystemError::GenericError(
                "No home directory detected for the current user.".to_owned(),
            )),
        }
    }
}

impl SystemLayout for NativeSystemLayout {
    fn configs_dir(&self) -> PathBuf {
        self.project.config_dir().to_owned()
    }

    fn cache_dir(&self) -> PathBuf {
        self.project.cache_dir().to_owned()
    }
}

#[derive(Debug)]
pub struct RootedSystemLayout {
    directory: PathBuf,
}

impl RootedSystemLayout {
    pub fn new<D: AsRef<Path>>(directory: D) -> Result<RootedSystemLayout, SystemError> {
        let directory = directory.as_ref();
        let directory = directory.to_owned();
        let layout = RootedSystemLayout { directory };

        if !layout.answers_config().exists() {
            if layout.configs_dir().join("answers.yaml").exists() {
                std::fs::rename(layout.configs_dir().join("answers.yaml"), layout.answers_config())?;
            }
        }

        Ok(layout)
    }
}

impl SystemLayout for RootedSystemLayout {
    fn configs_dir(&self) -> PathBuf {
        self.directory.clone().join("etc")
    }

    fn cache_dir(&self) -> PathBuf {
        self.directory.clone().join("var")
    }
}

impl Display for dyn SystemLayout {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        writeln!(f, "{}: {}", "Configs Directory", self.configs_dir().display())?;
        writeln!(f, "{}: {}", "User Answers", self.answers_config().display())?;
        writeln!(f, "{}: {}", "User Catalog", self.catalog().display())?;
        writeln!(f, "{}: {}", "Git Cache", self.git_cache_dir().display())?;
        writeln!(f, "{}: {}", "Catalog Cache", self.catalog_cache_dir().display())?;
        Ok(())
    }
}

pub fn dot_home_layout() -> Result<RootedSystemLayout, SystemError> {
    let result = directories::UserDirs::new().unwrap().home_dir().join(".archetect");
    Ok(RootedSystemLayout::new(result.to_str().unwrap().to_string())?)
}

pub fn temp_layout() -> Result<RootedSystemLayout, SystemError> {
    let temp_dir = tempdir()?;
    Ok(RootedSystemLayout::new(temp_dir.path())?)
}

#[derive(Debug, thiserror::Error)]
pub enum SystemError {
    #[error("IO System Error: {source}")]
    IOError {
        #[from]
        source: std::io::Error,
    },
    #[error("System Error: {0}")]
    GenericError(String),
}

impl From<String> for SystemError {
    fn from(error: String) -> Self {
        SystemError::GenericError(error)
    }
}