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 unsafe { std::env::remove_var("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 #[test]
158 fn test_state_dir_override() {
159 let test_dir = "/tmp/cuenv-test-state";
160 unsafe { std::env::set_var("CUENV_STATE_DIR", test_dir) };
161 let dir = state_dir().expect("state_dir should succeed");
162 assert_eq!(dir, PathBuf::from(test_dir));
163 unsafe { std::env::remove_var("CUENV_STATE_DIR") };
164 }
165
166 #[test]
167 fn test_cache_dir_default() {
168 unsafe { std::env::remove_var("CUENV_CACHE_DIR") };
169 let dir = cache_dir().expect("cache_dir should succeed");
170 assert!(dir.ends_with("cuenv"), "Should end with cuenv: {:?}", dir);
171 }
172
173 #[test]
174 fn test_cache_dir_override() {
175 let test_dir = "/tmp/cuenv-test-cache";
176 unsafe { std::env::set_var("CUENV_CACHE_DIR", test_dir) };
177 let dir = cache_dir().expect("cache_dir should succeed");
178 assert_eq!(dir, PathBuf::from(test_dir));
179 unsafe { std::env::remove_var("CUENV_CACHE_DIR") };
180 }
181
182 #[test]
183 fn test_runtime_dir_default() {
184 unsafe { std::env::remove_var("CUENV_RUNTIME_DIR") };
185 let dir = runtime_dir().expect("runtime_dir should succeed");
186 assert!(dir.ends_with("cuenv"), "Should end with cuenv: {:?}", dir);
187 }
188
189 #[test]
190 fn test_runtime_dir_override() {
191 let test_dir = "/tmp/cuenv-test-runtime";
192 unsafe { std::env::set_var("CUENV_RUNTIME_DIR", test_dir) };
193 let dir = runtime_dir().expect("runtime_dir should succeed");
194 assert_eq!(dir, PathBuf::from(test_dir));
195 unsafe { std::env::remove_var("CUENV_RUNTIME_DIR") };
196 }
197
198 #[test]
199 fn test_hook_state_dir() {
200 unsafe { std::env::remove_var("CUENV_STATE_DIR") };
201 let dir = hook_state_dir().expect("hook_state_dir should succeed");
202 assert!(dir.ends_with("state"), "Should end with state: {:?}", dir);
203 }
204
205 #[test]
206 fn test_approvals_file() {
207 unsafe { std::env::remove_var("CUENV_STATE_DIR") };
208 let file = approvals_file().expect("approvals_file should succeed");
209 assert!(
210 file.ends_with("approved.json"),
211 "Should end with approved.json: {:?}",
212 file
213 );
214 }
215
216 #[test]
217 fn test_task_cache_dir() {
218 unsafe { std::env::remove_var("CUENV_CACHE_DIR") };
219 let dir = task_cache_dir().expect("task_cache_dir should succeed");
220 assert!(dir.ends_with("tasks"), "Should end with tasks: {:?}", dir);
221 }
222
223 #[test]
224 fn test_coordinator_paths() {
225 unsafe { std::env::remove_var("CUENV_RUNTIME_DIR") };
226 let socket = coordinator_socket().expect("coordinator_socket should succeed");
227 let pid = coordinator_pid().expect("coordinator_pid should succeed");
228 let lock = coordinator_lock().expect("coordinator_lock should succeed");
229
230 assert!(
231 socket.ends_with("coordinator.sock"),
232 "Socket should end with coordinator.sock: {:?}",
233 socket
234 );
235 assert!(
236 pid.ends_with("coordinator.pid"),
237 "PID should end with coordinator.pid: {:?}",
238 pid
239 );
240 assert!(
241 lock.ends_with("coordinator.lock"),
242 "Lock should end with coordinator.lock: {:?}",
243 lock
244 );
245 }
246}