dev_prune/adapters/
pdm.rs1use super::{
11 BloatDir, EnforcePolicy, PackageManager, dir_size, enforce_two_tier, run_command_with_timeout,
12};
13use anyhow::Result;
14use std::fs;
15use std::path::Path;
16
17pub struct Pdm;
19
20const PDM_ENV_DIRS: [&str; 2] = [".venv", "__pypackages__"];
22
23impl PackageManager for Pdm {
24 fn name(&self) -> &'static str {
25 "pdm"
26 }
27
28 fn detect(&self, path: &Path) -> bool {
32 if path.join("pdm.lock").exists() {
33 return true;
34 }
35 fs::read_to_string(path.join("pyproject.toml"))
36 .is_ok_and(|c| c.contains("[tool.pdm]") || c.contains("pdm.backend"))
37 }
38
39 fn bloat_dirs(&self, path: &Path) -> Vec<BloatDir> {
40 PDM_ENV_DIRS
41 .iter()
42 .map(|name| (name, path.join(name)))
43 .filter(|(_, dir)| dir.is_dir())
44 .map(|(name, dir)| BloatDir {
45 name: (*name).to_string(),
46 path: dir.clone(),
47 size_bytes: dir_size(&dir),
48 shared_bytes: 0,
49 })
50 .collect()
51 }
52
53 fn enforce_lockfile(&self, path: &Path, policy: EnforcePolicy) -> Result<()> {
57 enforce_two_tier(
58 &path.join("pdm.lock"),
59 "pdm",
60 &["lock", "--check"],
61 &["lock"],
62 path,
63 policy,
64 )
65 }
66
67 fn restore(&self, path: &Path, timeout: std::time::Duration) -> Result<()> {
68 run_command_with_timeout("pdm", &["install"], path, timeout)
69 }
70
71 fn lockfiles(&self) -> &'static [&'static str] {
72 &["pdm.lock"]
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use tempfile::tempdir;
80
81 #[test]
82 fn detects_on_the_lockfile() {
83 let dir = tempdir().unwrap();
84 assert!(!Pdm.detect(dir.path()));
85 fs::write(dir.path().join("pdm.lock"), "").unwrap();
86 assert!(Pdm.detect(dir.path()));
87 }
88
89 #[test]
90 fn detects_on_a_pdm_pyproject() {
91 let dir = tempdir().unwrap();
92 fs::write(dir.path().join("pyproject.toml"), "[tool.pdm]\n").unwrap();
93 assert!(Pdm.detect(dir.path()));
94 }
95
96 #[test]
97 fn leaves_a_plain_pep621_project_alone() {
98 let dir = tempdir().unwrap();
99 fs::write(
100 dir.path().join("pyproject.toml"),
101 "[project]\nname = \"x\"\n",
102 )
103 .unwrap();
104 assert!(!Pdm.detect(dir.path()));
105 }
106
107 #[test]
108 fn claims_both_environment_layouts() {
109 let dir = tempdir().unwrap();
110 assert!(Pdm.bloat_dirs(dir.path()).is_empty());
111 fs::create_dir(dir.path().join(".venv")).unwrap();
112 fs::create_dir(dir.path().join("__pypackages__")).unwrap();
113 let names: Vec<String> = Pdm
114 .bloat_dirs(dir.path())
115 .into_iter()
116 .map(|b| b.name)
117 .collect();
118 assert_eq!(names, vec![".venv", "__pypackages__"]);
119 }
120}