1use std::path::{Path, PathBuf};
2
3pub struct ZLayerDirs {
7 data_dir: PathBuf,
8}
9
10impl ZLayerDirs {
11 pub fn new(data_dir: impl Into<PathBuf>) -> Self {
13 Self {
14 data_dir: data_dir.into(),
15 }
16 }
17
18 pub fn system_default() -> Self {
20 Self::new(Self::default_data_dir())
21 }
22
23 pub fn default_data_dir() -> PathBuf {
32 #[cfg(target_os = "macos")]
33 {
34 home_dir_or_tmp().join(".zlayer")
35 }
36 #[cfg(target_os = "windows")]
37 {
38 if let Some(local_app_data) = std::env::var_os("LOCALAPPDATA") {
39 PathBuf::from(local_app_data).join("ZLayer")
40 } else {
41 PathBuf::from(r"C:\ProgramData\ZLayer")
42 }
43 }
44 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
45 {
46 if is_root() {
47 PathBuf::from("/var/lib/zlayer")
48 } else {
49 home_dir_or_tmp().join(".zlayer")
50 }
51 }
52 }
53
54 pub fn default_run_dir() -> PathBuf {
60 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
61 {
62 PathBuf::from("/var/run/zlayer")
63 }
64 #[cfg(any(target_os = "macos", target_os = "windows"))]
65 {
66 Self::default_data_dir().join("run")
67 }
68 }
69
70 pub fn default_log_dir() -> PathBuf {
76 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
77 {
78 PathBuf::from("/var/log/zlayer")
79 }
80 #[cfg(any(target_os = "macos", target_os = "windows"))]
81 {
82 Self::default_data_dir().join("logs")
83 }
84 }
85
86 pub fn default_socket_path() -> String {
92 #[cfg(target_os = "windows")]
93 {
94 "tcp://127.0.0.1:3669".to_string()
95 }
96 #[cfg(not(target_os = "windows"))]
97 {
98 #[cfg(target_os = "macos")]
99 {
100 Self::default_data_dir()
101 .join("run")
102 .join("zlayer.sock")
103 .to_string_lossy()
104 .into_owned()
105 }
106 #[cfg(not(target_os = "macos"))]
107 {
108 "/var/run/zlayer.sock".to_string()
109 }
110 }
111 }
112
113 pub fn default_binary_dir() -> PathBuf {
122 #[cfg(unix)]
124 {
125 let probe = PathBuf::from("/usr/local/bin/.zlayer_write_probe");
126 if std::fs::write(&probe, b"").is_ok() {
127 let _ = std::fs::remove_file(&probe);
128 return PathBuf::from("/usr/local/bin");
129 }
130 }
131 let dirs = Self::system_default();
133 let bin_dir = dirs.bin();
134 let _ = std::fs::create_dir_all(&bin_dir);
135 bin_dir
136 }
137
138 pub fn data_dir(&self) -> &Path {
142 &self.data_dir
143 }
144
145 pub fn containers(&self) -> PathBuf {
147 self.data_dir.join("containers")
148 }
149
150 pub fn rootfs(&self) -> PathBuf {
152 self.data_dir.join("rootfs")
153 }
154
155 pub fn bundles(&self) -> PathBuf {
157 self.data_dir.join("bundles")
158 }
159
160 pub fn cache(&self) -> PathBuf {
162 self.data_dir.join("cache")
163 }
164
165 pub fn volumes(&self) -> PathBuf {
167 self.data_dir.join("volumes")
168 }
169
170 pub fn wasm(&self) -> PathBuf {
172 self.data_dir.join("wasm")
173 }
174
175 pub fn wasm_compiled(&self) -> PathBuf {
177 self.data_dir.join("wasm").join("compiled")
178 }
179
180 pub fn secrets(&self) -> PathBuf {
182 self.data_dir.join("secrets")
183 }
184
185 pub fn certs(&self) -> PathBuf {
187 self.data_dir.join("certs")
188 }
189
190 pub fn raft(&self) -> PathBuf {
192 self.data_dir.join("raft")
193 }
194
195 pub fn admin_password(&self) -> PathBuf {
197 self.data_dir.join("admin_password")
198 }
199
200 pub fn daemon_json(&self) -> PathBuf {
202 self.data_dir.join("daemon.json")
203 }
204
205 pub fn logs(&self) -> PathBuf {
208 self.data_dir.join("logs")
209 }
210
211 pub fn vms(&self) -> PathBuf {
215 self.data_dir.join("vms")
216 }
217
218 pub fn images(&self) -> PathBuf {
220 self.data_dir.join("images")
221 }
222
223 pub fn bin(&self) -> PathBuf {
225 self.data_dir.join("bin")
226 }
227
228 pub fn toolchain_cache(&self) -> PathBuf {
230 self.data_dir.join("toolchain-cache")
231 }
232
233 pub fn tmp(&self) -> PathBuf {
235 self.data_dir.join("tmp")
236 }
237}
238
239fn home_dir_or_tmp() -> PathBuf {
242 std::env::var_os("HOME")
243 .map(PathBuf::from)
244 .unwrap_or_else(|| PathBuf::from("/tmp"))
245}
246
247#[cfg(not(any(target_os = "macos", target_os = "windows")))]
248fn is_root() -> bool {
249 #[cfg(unix)]
250 {
251 nix::unistd::geteuid().is_root()
252 }
253 #[cfg(not(unix))]
254 {
255 false
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn subdirectories_are_relative_to_data_dir() {
265 let dirs = ZLayerDirs::new("/test/data");
266 assert_eq!(dirs.containers(), PathBuf::from("/test/data/containers"));
267 assert_eq!(dirs.rootfs(), PathBuf::from("/test/data/rootfs"));
268 assert_eq!(dirs.bundles(), PathBuf::from("/test/data/bundles"));
269 assert_eq!(dirs.cache(), PathBuf::from("/test/data/cache"));
270 assert_eq!(dirs.volumes(), PathBuf::from("/test/data/volumes"));
271 assert_eq!(dirs.wasm(), PathBuf::from("/test/data/wasm"));
272 assert_eq!(
273 dirs.wasm_compiled(),
274 PathBuf::from("/test/data/wasm/compiled")
275 );
276 assert_eq!(dirs.secrets(), PathBuf::from("/test/data/secrets"));
277 assert_eq!(dirs.certs(), PathBuf::from("/test/data/certs"));
278 assert_eq!(dirs.raft(), PathBuf::from("/test/data/raft"));
279 assert_eq!(
280 dirs.admin_password(),
281 PathBuf::from("/test/data/admin_password")
282 );
283 assert_eq!(dirs.daemon_json(), PathBuf::from("/test/data/daemon.json"));
284 assert_eq!(dirs.logs(), PathBuf::from("/test/data/logs"));
285 assert_eq!(dirs.vms(), PathBuf::from("/test/data/vms"));
286 assert_eq!(dirs.images(), PathBuf::from("/test/data/images"));
287 assert_eq!(dirs.bin(), PathBuf::from("/test/data/bin"));
288 assert_eq!(
289 dirs.toolchain_cache(),
290 PathBuf::from("/test/data/toolchain-cache")
291 );
292 assert_eq!(dirs.tmp(), PathBuf::from("/test/data/tmp"));
293 }
294
295 #[test]
296 fn system_default_uses_default_data_dir() {
297 let dirs = ZLayerDirs::system_default();
298 assert_eq!(dirs.data_dir(), ZLayerDirs::default_data_dir().as_path());
299 }
300}