Skip to main content

gen_core/
config.rs

1use std::{
2    env, fs,
3    path::{Path, PathBuf},
4};
5
6use crate::{HashId, errors::ConfigError};
7
8pub const CHANGESET_DIR_NAME: &str = "changesets";
9
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct Workspace {
12    base_dir: PathBuf,
13}
14
15impl Workspace {
16    pub fn new(base_dir: impl Into<PathBuf>) -> Self {
17        Self {
18            base_dir: base_dir.into(),
19        }
20    }
21
22    pub fn from_current_dir() -> Self {
23        Self::new(env::current_dir().unwrap())
24    }
25
26    pub fn base_dir(&self) -> &Path {
27        &self.base_dir
28    }
29
30    pub fn ensure_gen_dir(&self) -> PathBuf {
31        let gen_path = self.base_dir.join(".gen");
32        ensure_dir(&gen_path);
33        let changesets = gen_path.join(CHANGESET_DIR_NAME);
34        ensure_dir(&changesets);
35        gen_path
36    }
37
38    pub fn find_gen_dir(&self) -> Option<PathBuf> {
39        let mut cur_dir = self.base_dir.as_path();
40        let mut gen_path = cur_dir.join(".gen");
41        while !gen_path.is_dir() {
42            cur_dir = cur_dir.parent()?;
43            gen_path = cur_dir.join(".gen");
44        }
45        Some(gen_path)
46    }
47
48    pub fn repo_root(&self) -> Result<PathBuf, ConfigError> {
49        let gen_dir = self
50            .find_gen_dir()
51            .ok_or(ConfigError::GenDirectoryNotFound)?;
52
53        gen_dir
54            .parent()
55            .map(Path::to_path_buf)
56            .ok_or(ConfigError::RepoRootNotFound)
57    }
58
59    pub fn gen_db_path(&self) -> Result<PathBuf, ConfigError> {
60        self.find_gen_dir()
61            .map(|dir| dir.join("gen.db"))
62            .ok_or(ConfigError::GenDirectoryNotFound)
63    }
64
65    pub fn changeset_path(&self, hash: &HashId) -> PathBuf {
66        let path = self
67            .ensure_gen_dir()
68            .join(CHANGESET_DIR_NAME)
69            .join(format!("{hash}"));
70        ensure_dir(&path);
71        path
72    }
73}
74
75fn ensure_dir(path: &Path) {
76    if !path.is_dir() {
77        fs::create_dir_all(path).unwrap();
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use std::fs;
84
85    use tempfile::tempdir;
86
87    use super::*;
88
89    #[test]
90    fn ensure_gen_dir_creates_directory() {
91        let tmp_dir = tempdir().unwrap();
92        let tmp_dir_path = tmp_dir.path().to_path_buf();
93        let workspace = Workspace::new(&tmp_dir_path);
94
95        let gen_dir = workspace.ensure_gen_dir();
96
97        assert_eq!(gen_dir, tmp_dir_path.join(".gen"));
98        assert!(gen_dir.is_dir());
99    }
100
101    #[test]
102    fn find_gen_dir_walks_up_tree() {
103        let tmp_dir = tempdir().unwrap();
104        let tmp_dir_path = tmp_dir.path().to_path_buf();
105        let root_workspace = Workspace::new(&tmp_dir_path);
106        let gen_dir = root_workspace.ensure_gen_dir();
107
108        let nested_dir = tmp_dir_path.join("nested").join("deep");
109        fs::create_dir_all(&nested_dir).unwrap();
110        let nested_workspace = Workspace::new(&nested_dir);
111
112        assert_eq!(nested_workspace.find_gen_dir(), Some(gen_dir));
113    }
114
115    #[test]
116    fn repo_root_returns_parent_of_gen_dir() {
117        let tmp_dir = tempdir().unwrap();
118        let tmp_dir_path = tmp_dir.path().to_path_buf();
119        let workspace = Workspace::new(&tmp_dir_path);
120        workspace.ensure_gen_dir();
121
122        assert_eq!(workspace.repo_root().unwrap(), tmp_dir_path);
123    }
124
125    #[test]
126    fn repo_root_errors_when_missing_gen_dir() {
127        let tmp_dir = tempdir().unwrap();
128        let tmp_dir_path = tmp_dir.path().to_path_buf();
129        let workspace = Workspace::new(&tmp_dir_path);
130
131        assert_eq!(
132            Err(ConfigError::GenDirectoryNotFound),
133            workspace.repo_root()
134        );
135    }
136
137    #[test]
138    fn gen_db_path_resolves_inside_gen_dir() {
139        let tmp_dir = tempdir().unwrap();
140        let tmp_dir_path = tmp_dir.path().to_path_buf();
141        let workspace = Workspace::new(&tmp_dir_path);
142        let gen_dir = workspace.ensure_gen_dir();
143
144        assert_eq!(workspace.gen_db_path().unwrap(), gen_dir.join("gen.db"));
145    }
146
147    #[test]
148    fn changeset_path_creates_directory_for_hash() {
149        let tmp_dir = tempdir().unwrap();
150        let tmp_dir_path = tmp_dir.path().to_path_buf();
151        let workspace = Workspace::new(&tmp_dir_path);
152        let hash = HashId([0; 32]);
153
154        let path = workspace.changeset_path(&hash);
155
156        assert_eq!(
157            path,
158            tmp_dir_path
159                .join(".gen")
160                .join(CHANGESET_DIR_NAME)
161                .join(format!("{hash}"))
162        );
163        assert!(path.is_dir());
164    }
165}