dev_prune/adapters/
cargo_adapter.rs1use super::{BloatDir, EnforcePolicy, PackageManager, dir_size, enforce_two_tier};
7use anyhow::Result;
8use std::path::Path;
9
10pub struct Cargo;
12
13impl PackageManager for Cargo {
14 fn name(&self) -> &'static str {
15 "cargo"
16 }
17
18 fn detect(&self, path: &Path) -> bool {
19 path.join("Cargo.toml").exists()
20 }
21
22 fn bloat_dirs(&self, path: &Path) -> Vec<BloatDir> {
23 let mut dirs = Vec::new();
24 let target_path = path.join("target");
25 if target_path.exists() {
26 dirs.push(BloatDir {
27 name: "target".to_string(),
28 path: target_path.clone(),
29 size_bytes: dir_size(&target_path),
30 });
31 }
32 dirs
33 }
34
35 fn enforce_lockfile(&self, path: &Path, policy: EnforcePolicy) -> Result<()> {
43 enforce_two_tier(
44 &path.join("Cargo.lock"),
45 "cargo",
46 &["metadata", "--locked", "--format-version", "1"],
47 &["generate-lockfile"],
48 path,
49 policy,
50 )
51 }
52
53 fn restore(&self, _path: &Path) -> Result<()> {
54 println!("Rust target/ will regenerate on next cargo build");
55 Ok(())
56 }
57
58 fn lockfiles(&self) -> &'static [&'static str] {
59 &["Cargo.lock"]
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use std::fs;
67 use std::fs::File;
68 use tempfile::tempdir;
69
70 #[test]
71 fn test_name() {
72 let adapter = Cargo;
73 assert_eq!(adapter.name(), "cargo");
74 }
75
76 #[test]
83 fn a_default_pass_never_rewrites_a_stale_lockfile() {
84 if !super::super::binary_available("cargo") {
85 return;
86 }
87 let dir = tempdir().unwrap();
88 fs::write(
89 dir.path().join("Cargo.toml"),
90 "[package]\nname = \"stale\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n\
91 [dependencies]\nserde = \"1\"\n",
92 )
93 .unwrap();
94 fs::create_dir(dir.path().join("src")).unwrap();
95 fs::write(dir.path().join("src").join("lib.rs"), "").unwrap();
96
97 let stale = "version = 3\n\n[[package]]\nname = \"stale\"\nversion = \"0.1.0\"\n";
100 fs::write(dir.path().join("Cargo.lock"), stale).unwrap();
101
102 let result = Cargo.enforce_lockfile(dir.path(), EnforcePolicy::default());
103
104 assert!(
105 result.is_err(),
106 "a lockfile that cannot resolve the manifest must not pass verification"
107 );
108 assert_eq!(
109 fs::read_to_string(dir.path().join("Cargo.lock")).unwrap(),
110 stale,
111 "the read-only verification rewrote Cargo.lock"
112 );
113 }
114
115 #[test]
116 fn test_detect_positive() {
117 let dir = tempdir().unwrap();
118 File::create(dir.path().join("Cargo.toml")).unwrap();
119
120 let adapter = Cargo;
121 assert!(adapter.detect(dir.path()));
122 }
123
124 #[test]
125 fn test_detect_negative() {
126 let dir = tempdir().unwrap();
127
128 let adapter = Cargo;
129 assert!(!adapter.detect(dir.path()));
130 }
131
132 #[test]
133 fn test_bloat_dirs_present() {
134 let dir = tempdir().unwrap();
135 fs::create_dir(dir.path().join("target")).unwrap();
136
137 let adapter = Cargo;
138 let dirs = adapter.bloat_dirs(dir.path());
139 assert_eq!(dirs.len(), 1);
140 assert_eq!(dirs[0].name, "target");
141 }
142
143 #[test]
144 fn test_bloat_dirs_absent() {
145 let dir = tempdir().unwrap();
146
147 let adapter = Cargo;
148 let dirs = adapter.bloat_dirs(dir.path());
149 assert!(dirs.is_empty());
150 }
151}