archetect_core/
system.rs

1use crate::config::CATALOG_FILE_NAME;
2use directories::ProjectDirs;
3use std::fmt::{Display, Error, Formatter};
4use std::path::{Path, PathBuf};
5use tempfile::tempdir;
6
7pub enum LayoutType {
8    Native,
9    DotHome,
10    Temp,
11}
12
13pub trait SystemLayout {
14    fn configs_dir(&self) -> PathBuf;
15
16    fn cache_dir(&self) -> PathBuf;
17
18    fn catalog_cache_dir(&self) -> PathBuf {
19        self.cache_dir().join("catalogs")
20    }
21
22    fn git_cache_dir(&self) -> PathBuf {
23        self.cache_dir().join("git")
24    }
25
26    fn http_cache_dir(&self) -> PathBuf {
27        self.cache_dir().join("http")
28    }
29
30    fn answers_config(&self) -> PathBuf {
31        self.configs_dir().join("answers.yml")
32    }
33
34    fn catalog(&self) -> PathBuf {
35        self.configs_dir().join(CATALOG_FILE_NAME)
36    }
37}
38
39#[derive(Debug)]
40pub struct NativeSystemLayout {
41    project: ProjectDirs,
42}
43
44impl NativeSystemLayout {
45    pub fn new() -> Result<NativeSystemLayout, SystemError> {
46        match ProjectDirs::from("", "", "archetect") {
47            Some(project) => Ok(NativeSystemLayout { project }),
48            None => Err(SystemError::GenericError(
49                "No home directory detected for the current user.".to_owned(),
50            )),
51        }
52    }
53}
54
55impl SystemLayout for NativeSystemLayout {
56    fn configs_dir(&self) -> PathBuf {
57        self.project.config_dir().to_owned()
58    }
59
60    fn cache_dir(&self) -> PathBuf {
61        self.project.cache_dir().to_owned()
62    }
63}
64
65#[derive(Debug)]
66pub struct RootedSystemLayout {
67    directory: PathBuf,
68}
69
70impl RootedSystemLayout {
71    pub fn new<D: AsRef<Path>>(directory: D) -> Result<RootedSystemLayout, SystemError> {
72        let directory = directory.as_ref();
73        let directory = directory.to_owned();
74        let layout = RootedSystemLayout { directory };
75
76        if !layout.answers_config().exists() {
77            if layout.configs_dir().join("answers.yaml").exists() {
78                std::fs::rename(layout.configs_dir().join("answers.yaml"), layout.answers_config())?;
79            }
80        }
81
82        Ok(layout)
83    }
84}
85
86impl SystemLayout for RootedSystemLayout {
87    fn configs_dir(&self) -> PathBuf {
88        self.directory.clone().join("etc")
89    }
90
91    fn cache_dir(&self) -> PathBuf {
92        self.directory.clone().join("var")
93    }
94}
95
96impl Display for dyn SystemLayout {
97    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
98        writeln!(f, "{}: {}", "Configs Directory", self.configs_dir().display())?;
99        writeln!(f, "{}: {}", "User Answers", self.answers_config().display())?;
100        writeln!(f, "{}: {}", "User Catalog", self.catalog().display())?;
101        writeln!(f, "{}: {}", "Git Cache", self.git_cache_dir().display())?;
102        writeln!(f, "{}: {}", "Catalog Cache", self.catalog_cache_dir().display())?;
103        Ok(())
104    }
105}
106
107pub fn dot_home_layout() -> Result<RootedSystemLayout, SystemError> {
108    let result = directories::UserDirs::new().unwrap().home_dir().join(".archetect");
109    Ok(RootedSystemLayout::new(result.to_str().unwrap().to_string())?)
110}
111
112pub fn temp_layout() -> Result<RootedSystemLayout, SystemError> {
113    let temp_dir = tempdir()?;
114    Ok(RootedSystemLayout::new(temp_dir.path())?)
115}
116
117#[derive(Debug, thiserror::Error)]
118pub enum SystemError {
119    #[error("IO System Error: {source}")]
120    IOError {
121        #[from]
122        source: std::io::Error,
123    },
124    #[error("System Error: {0}")]
125    GenericError(String),
126}
127
128impl From<String> for SystemError {
129    fn from(error: String) -> Self {
130        SystemError::GenericError(error)
131    }
132}