1use crate::{Error, Result};
17use std::path::PathBuf;
18
19pub fn state_dir() -> Result<PathBuf> {
33 if let Ok(dir) = std::env::var("CUENV_STATE_DIR")
34 && !dir.is_empty()
35 {
36 return Ok(PathBuf::from(dir));
37 }
38
39 let base = dirs::state_dir()
46 .or_else(dirs::data_dir)
47 .ok_or_else(|| Error::configuration("Could not determine state directory"))?;
48
49 Ok(base.join("cuenv"))
50}
51
52pub fn cache_dir() -> Result<PathBuf> {
66 if let Ok(dir) = std::env::var("CUENV_CACHE_DIR")
67 && !dir.is_empty()
68 {
69 return Ok(PathBuf::from(dir));
70 }
71
72 let base = dirs::cache_dir()
73 .ok_or_else(|| Error::configuration("Could not determine cache directory"))?;
74
75 Ok(base.join("cuenv"))
76}
77
78pub fn runtime_dir() -> Result<PathBuf> {
93 if let Ok(dir) = std::env::var("CUENV_RUNTIME_DIR")
94 && !dir.is_empty()
95 {
96 return Ok(PathBuf::from(dir));
97 }
98
99 let base = dirs::runtime_dir().unwrap_or_else(std::env::temp_dir);
103
104 Ok(base.join("cuenv"))
105}
106
107pub fn hook_state_dir() -> Result<PathBuf> {
111 Ok(state_dir()?.join("state"))
112}
113
114pub fn approvals_file() -> Result<PathBuf> {
118 Ok(state_dir()?.join("approved.json"))
119}
120
121pub fn task_cache_dir() -> Result<PathBuf> {
125 Ok(cache_dir()?.join("tasks"))
126}
127
128pub fn coordinator_socket() -> Result<PathBuf> {
132 Ok(runtime_dir()?.join("coordinator.sock"))
133}
134
135pub fn coordinator_pid() -> Result<PathBuf> {
137 Ok(runtime_dir()?.join("coordinator.pid"))
138}
139
140pub fn coordinator_lock() -> Result<PathBuf> {
142 Ok(runtime_dir()?.join("coordinator.lock"))
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_state_dir_default() {
151 temp_env::with_var_unset("CUENV_STATE_DIR", || {
153 let dir = state_dir().expect("state_dir should succeed");
154 assert!(dir.ends_with("cuenv"), "Should end with cuenv: {:?}", dir);
155 });
156 }
157
158 #[test]
159 fn test_state_dir_override() {
160 let test_dir = "/tmp/cuenv-test-state";
161 temp_env::with_var("CUENV_STATE_DIR", Some(test_dir), || {
162 let dir = state_dir().expect("state_dir should succeed");
163 assert_eq!(dir, PathBuf::from(test_dir));
164 });
165 }
166
167 #[test]
168 fn test_cache_dir_default() {
169 temp_env::with_var_unset("CUENV_CACHE_DIR", || {
170 let dir = cache_dir().expect("cache_dir should succeed");
171 assert!(dir.ends_with("cuenv"), "Should end with cuenv: {:?}", dir);
172 });
173 }
174
175 #[test]
176 fn test_cache_dir_override() {
177 let test_dir = "/tmp/cuenv-test-cache";
178 temp_env::with_var("CUENV_CACHE_DIR", Some(test_dir), || {
179 let dir = cache_dir().expect("cache_dir should succeed");
180 assert_eq!(dir, PathBuf::from(test_dir));
181 });
182 }
183
184 #[test]
185 fn test_runtime_dir_default() {
186 temp_env::with_var_unset("CUENV_RUNTIME_DIR", || {
187 let dir = runtime_dir().expect("runtime_dir should succeed");
188 assert!(dir.ends_with("cuenv"), "Should end with cuenv: {:?}", dir);
189 });
190 }
191
192 #[test]
193 fn test_runtime_dir_override() {
194 let test_dir = "/tmp/cuenv-test-runtime";
195 temp_env::with_var("CUENV_RUNTIME_DIR", Some(test_dir), || {
196 let dir = runtime_dir().expect("runtime_dir should succeed");
197 assert_eq!(dir, PathBuf::from(test_dir));
198 });
199 }
200
201 #[test]
202 fn test_hook_state_dir() {
203 temp_env::with_var_unset("CUENV_STATE_DIR", || {
204 let dir = hook_state_dir().expect("hook_state_dir should succeed");
205 assert!(dir.ends_with("state"), "Should end with state: {:?}", dir);
206 });
207 }
208
209 #[test]
210 fn test_approvals_file() {
211 temp_env::with_var_unset("CUENV_STATE_DIR", || {
212 let file = approvals_file().expect("approvals_file should succeed");
213 assert!(
214 file.ends_with("approved.json"),
215 "Should end with approved.json: {:?}",
216 file
217 );
218 });
219 }
220
221 #[test]
222 fn test_task_cache_dir() {
223 temp_env::with_var_unset("CUENV_CACHE_DIR", || {
224 let dir = task_cache_dir().expect("task_cache_dir should succeed");
225 assert!(dir.ends_with("tasks"), "Should end with tasks: {:?}", dir);
226 });
227 }
228
229 #[test]
230 fn test_coordinator_paths() {
231 temp_env::with_var_unset("CUENV_RUNTIME_DIR", || {
232 let socket = coordinator_socket().expect("coordinator_socket should succeed");
233 let pid = coordinator_pid().expect("coordinator_pid should succeed");
234 let lock = coordinator_lock().expect("coordinator_lock should succeed");
235
236 assert!(
237 socket.ends_with("coordinator.sock"),
238 "Socket should end with coordinator.sock: {:?}",
239 socket
240 );
241 assert!(
242 pid.ends_with("coordinator.pid"),
243 "PID should end with coordinator.pid: {:?}",
244 pid
245 );
246 assert!(
247 lock.ends_with("coordinator.lock"),
248 "Lock should end with coordinator.lock: {:?}",
249 lock
250 );
251 });
252 }
253}