1use std::path::PathBuf;
18
19pub fn config_dir() -> PathBuf {
23 if let Ok(config) = std::env::var("XDG_CONFIG_HOME") {
24 PathBuf::from(config).join("tau")
25 } else if let Ok(home) = std::env::var("HOME") {
26 PathBuf::from(home).join(".config").join("tau")
27 } else {
28 PathBuf::from("/tmp").join("tau-config")
29 }
30}
31
32pub fn data_dir() -> PathBuf {
36 if let Ok(data) = std::env::var("XDG_DATA_HOME") {
37 PathBuf::from(data).join("tau")
38 } else if let Ok(home) = std::env::var("HOME") {
39 PathBuf::from(home).join(".local").join("share").join("tau")
40 } else {
41 PathBuf::from("/tmp").join("tau-data")
42 }
43}
44
45pub fn runtime_dir() -> PathBuf {
49 if let Ok(dir) = std::env::var("XDG_RUNTIME_DIR") {
50 PathBuf::from(dir).join("tau")
51 } else if let Ok(home) = std::env::var("HOME") {
52 PathBuf::from(home).join(".tau")
53 } else {
54 PathBuf::from("/tmp").join(format!("tau-{}", std::process::id()))
55 }
56}
57
58pub fn state_dir() -> PathBuf {
65 if let Ok(state) = std::env::var("XDG_STATE_HOME") {
66 PathBuf::from(state).join("tau")
67 } else if let Ok(home) = std::env::var("HOME") {
68 PathBuf::from(home).join(".local").join("state").join("tau")
69 } else {
70 PathBuf::from("/tmp").join("tau-state")
71 }
72}
73
74pub fn logs_dir() -> PathBuf {
76 state_dir().join("logs")
77}
78
79pub fn socket_path() -> PathBuf {
81 runtime_dir().join("tau.sock")
82}
83
84pub fn pid_path() -> PathBuf {
86 let mut p = socket_path();
87 p.set_file_name("tau.pid");
88 p
89}
90
91pub fn project_config_dir(name: &str) -> PathBuf {
95 config_dir().join("projects").join(name)
96}
97
98pub fn is_running() -> bool {
100 std::os::unix::net::UnixStream::connect(socket_path()).is_ok()
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 use crate::TEST_ENV_MUTEX as ENV_LOCK;
113
114 struct EnvSnapshot {
115 xdg_state: Option<String>,
116 xdg_config: Option<String>,
117 xdg_data: Option<String>,
118 xdg_runtime: Option<String>,
119 home: Option<String>,
120 }
121
122 impl EnvSnapshot {
123 fn capture() -> Self {
124 Self {
125 xdg_state: std::env::var("XDG_STATE_HOME").ok(),
126 xdg_config: std::env::var("XDG_CONFIG_HOME").ok(),
127 xdg_data: std::env::var("XDG_DATA_HOME").ok(),
128 xdg_runtime: std::env::var("XDG_RUNTIME_DIR").ok(),
129 home: std::env::var("HOME").ok(),
130 }
131 }
132
133 fn restore(self) {
134 fn set(k: &str, v: Option<String>) {
135 unsafe {
138 match v {
139 Some(v) => std::env::set_var(k, v),
140 None => std::env::remove_var(k),
141 }
142 }
143 }
144 set("XDG_STATE_HOME", self.xdg_state);
145 set("XDG_CONFIG_HOME", self.xdg_config);
146 set("XDG_DATA_HOME", self.xdg_data);
147 set("XDG_RUNTIME_DIR", self.xdg_runtime);
148 set("HOME", self.home);
149 }
150 }
151
152 fn set_var(k: &str, v: &str) {
153 unsafe {
155 std::env::set_var(k, v);
156 }
157 }
158
159 fn remove_var(k: &str) {
160 unsafe {
162 std::env::remove_var(k);
163 }
164 }
165
166 #[test]
167 fn state_dir_uses_xdg_state_home_when_set() {
168 let _g = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
169 let snap = EnvSnapshot::capture();
170 set_var("XDG_STATE_HOME", "/custom/state");
171 set_var("HOME", "/home/ignored");
172 assert_eq!(state_dir(), PathBuf::from("/custom/state/tau"));
173 assert_eq!(logs_dir(), PathBuf::from("/custom/state/tau/logs"));
174 snap.restore();
175 }
176
177 #[test]
178 fn state_dir_uses_home_when_xdg_unset() {
179 let _g = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
180 let snap = EnvSnapshot::capture();
181 remove_var("XDG_STATE_HOME");
182 set_var("HOME", "/home/alice");
183 assert_eq!(state_dir(), PathBuf::from("/home/alice/.local/state/tau"));
184 assert_eq!(
185 logs_dir(),
186 PathBuf::from("/home/alice/.local/state/tau/logs")
187 );
188 snap.restore();
189 }
190
191 #[test]
192 fn state_dir_falls_back_to_tmp_when_neither_set() {
193 let _g = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
194 let snap = EnvSnapshot::capture();
195 remove_var("XDG_STATE_HOME");
196 remove_var("HOME");
197 assert_eq!(state_dir(), PathBuf::from("/tmp/tau-state"));
198 assert_eq!(logs_dir(), PathBuf::from("/tmp/tau-state/logs"));
199 snap.restore();
200 }
201}