dev_prune/adapters/
zig.rs1use super::{BloatDir, EnforcePolicy, PackageManager, dir_size};
18use anyhow::{Result, anyhow};
19use std::fs;
20use std::path::Path;
21
22const MANIFEST: &str = "build.zig";
24
25const CACHE_DIRS: &[&str] = &[".zig-cache", "zig-cache", "zig-out"];
28
29pub struct Zig;
31
32impl PackageManager for Zig {
33 fn name(&self) -> &'static str {
34 "zig"
35 }
36
37 fn detect(&self, path: &Path) -> bool {
38 path.join(MANIFEST).is_file()
39 }
40
41 fn bloat_dirs(&self, path: &Path) -> Vec<BloatDir> {
42 CACHE_DIRS
43 .iter()
44 .map(|name| path.join(name))
45 .filter(|dir| dir.is_dir())
46 .map(|dir| BloatDir {
47 name: dir
48 .file_name()
49 .unwrap_or_default()
50 .to_string_lossy()
51 .into_owned(),
52 size_bytes: dir_size(&dir),
53 path: dir,
54 shared_bytes: 0,
55 })
56 .collect()
57 }
58
59 fn enforce_lockfile(&self, path: &Path, _policy: EnforcePolicy) -> Result<()> {
66 let manifest = path.join(MANIFEST);
67 let content = fs::read_to_string(&manifest).map_err(|e| {
68 anyhow!("`{MANIFEST}` could not be read ({e}): nothing to rebuild the caches from.")
69 })?;
70 if !content.contains("pub fn build") {
71 return Err(anyhow!(
72 "`{MANIFEST}` has no `pub fn build` entry point: refusing to treat the \
73 build caches as regenerable from it."
74 ));
75 }
76 Ok(())
77 }
78
79 fn restore(&self, _path: &Path, _timeout: std::time::Duration) -> Result<()> {
80 println!("Zig's caches and outputs regenerate on the next `zig build`");
81 Ok(())
82 }
83
84 fn lockfiles(&self) -> &'static [&'static str] {
85 &[MANIFEST]
86 }
87
88 fn opt_in(&self) -> bool {
89 true
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use std::path::PathBuf;
97 use tempfile::tempdir;
98
99 fn project(dir: &Path) -> PathBuf {
101 fs::write(
102 dir.join(MANIFEST),
103 "const std = @import(\"std\");\n\npub fn build(b: *std.Build) void {\n _ = b;\n}\n",
104 )
105 .unwrap();
106 dir.to_path_buf()
107 }
108
109 fn claimed(project: &Path) -> Vec<String> {
110 Zig.bloat_dirs(project)
111 .into_iter()
112 .map(|b| b.name)
113 .collect()
114 }
115
116 #[test]
117 fn detects_on_the_build_script() {
118 let dir = tempdir().unwrap();
119 assert!(!Zig.detect(dir.path()));
120 project(dir.path());
121 assert!(Zig.detect(dir.path()));
122 }
123
124 #[test]
125 fn caches_and_install_output_are_claimed() {
126 let dir = tempdir().unwrap();
129 let root = project(dir.path());
130 fs::create_dir(root.join(".zig-cache")).unwrap();
131 fs::create_dir(root.join("zig-cache")).unwrap();
132 fs::create_dir(root.join("zig-out")).unwrap();
133
134 assert_eq!(claimed(&root), vec![".zig-cache", "zig-cache", "zig-out"]);
135 }
136
137 #[test]
138 fn nothing_else_beside_the_manifest_is_claimed() {
139 let dir = tempdir().unwrap();
140 let root = project(dir.path());
141 fs::create_dir(root.join("src")).unwrap();
142
143 assert!(claimed(&root).is_empty());
144 }
145
146 #[test]
147 fn a_missing_or_bogus_build_script_is_refused() {
148 let dir = tempdir().unwrap();
149 let policy = EnforcePolicy::default();
150 assert!(Zig.enforce_lockfile(dir.path(), policy).is_err());
151 fs::write(dir.path().join(MANIFEST), "hello there").unwrap();
152 assert!(Zig.enforce_lockfile(dir.path(), policy).is_err());
153 project(dir.path());
154 assert!(Zig.enforce_lockfile(dir.path(), policy).is_ok());
155 }
156
157 #[test]
158 fn zig_is_opt_in() {
159 assert!(Zig.opt_in());
160 }
161}