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}