dev_prune/adapters/
cabal.rs1use super::{BloatDir, EnforcePolicy, PackageManager, dir_size};
21use anyhow::{Result, anyhow};
22use std::fs;
23use std::path::Path;
24
25const MANIFEST: &str = "cabal.project";
27
28const CACHE_DIRS: &[&str] = &["dist-newstyle"];
30
31pub struct Cabal;
33
34impl PackageManager for Cabal {
35 fn name(&self) -> &'static str {
36 "cabal"
37 }
38
39 fn detect(&self, path: &Path) -> bool {
40 path.join(MANIFEST).is_file()
41 }
42
43 fn bloat_dirs(&self, path: &Path) -> Vec<BloatDir> {
44 CACHE_DIRS
45 .iter()
46 .map(|name| path.join(name))
47 .filter(|dir| dir.is_dir())
48 .map(|dir| BloatDir {
49 name: dir
50 .file_name()
51 .unwrap_or_default()
52 .to_string_lossy()
53 .into_owned(),
54 size_bytes: dir_size(&dir),
55 path: dir,
56 shared_bytes: 0,
57 })
58 .collect()
59 }
60
61 fn enforce_lockfile(&self, path: &Path, _policy: EnforcePolicy) -> Result<()> {
67 let manifest = path.join(MANIFEST);
68 let content = fs::read_to_string(&manifest).map_err(|e| {
69 anyhow!(
70 "`{MANIFEST}` could not be read ({e}): nothing to rebuild `dist-newstyle` from."
71 )
72 })?;
73 if !content.contains("packages") {
74 return Err(anyhow!(
75 "`{MANIFEST}` declares no `packages:`: refusing to treat the build tree \
76 as regenerable from it."
77 ));
78 }
79 Ok(())
80 }
81
82 fn restore(&self, _path: &Path, _timeout: std::time::Duration) -> Result<()> {
83 println!("Cabal's build tree regenerates on the next `cabal build`");
84 Ok(())
85 }
86
87 fn lockfiles(&self) -> &'static [&'static str] {
88 &[MANIFEST]
89 }
90
91 fn opt_in(&self) -> bool {
92 true
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use std::path::PathBuf;
100 use tempfile::tempdir;
101
102 fn project(dir: &Path) -> PathBuf {
104 fs::write(dir.join(MANIFEST), "packages: .\n").unwrap();
105 dir.to_path_buf()
106 }
107
108 fn claimed(project: &Path) -> Vec<String> {
109 Cabal
110 .bloat_dirs(project)
111 .into_iter()
112 .map(|b| b.name)
113 .collect()
114 }
115
116 #[test]
117 fn detects_on_the_project_file() {
118 let dir = tempdir().unwrap();
119 assert!(!Cabal.detect(dir.path()));
120 project(dir.path());
121 assert!(Cabal.detect(dir.path()));
122 }
123
124 #[test]
125 fn the_build_tree_is_claimed_and_nothing_else() {
126 let dir = tempdir().unwrap();
127 let root = project(dir.path());
128 fs::create_dir(root.join("dist-newstyle")).unwrap();
129 fs::create_dir(root.join("src")).unwrap();
130
131 assert_eq!(claimed(&root), vec!["dist-newstyle"]);
132 }
133
134 #[test]
135 fn a_missing_or_packageless_project_file_is_refused() {
136 let dir = tempdir().unwrap();
137 let policy = EnforcePolicy::default();
138 assert!(Cabal.enforce_lockfile(dir.path(), policy).is_err());
139 fs::write(dir.path().join(MANIFEST), "-- just a comment\n").unwrap();
140 assert!(Cabal.enforce_lockfile(dir.path(), policy).is_err());
141 project(dir.path());
142 assert!(Cabal.enforce_lockfile(dir.path(), policy).is_ok());
143 }
144
145 #[test]
146 fn cabal_is_opt_in() {
147 assert!(Cabal.opt_in());
148 }
149}