1use super::errors::BridgeError;
7use std::fs;
8use std::path::PathBuf;
9
10#[cfg(windows)]
12const PYTHON_LIB_DIR: &str = "Lib";
13#[cfg(unix)]
14const PYTHON_LIB_DIR: &str = "lib";
15
16#[cfg(windows)]
18const PYTHON_BIN_DIR: &str = "Scripts";
19#[cfg(unix)]
20const PYTHON_BIN_DIR: &str = "bin";
21
22#[cfg(unix)]
24const PYTHON_EXE_CANDIDATES: &[&str] = &["python3", "python"];
25#[cfg(windows)]
26const PYTHON_EXE_CANDIDATES: &[&str] = &["python.exe", "python3.exe", "python3.12.exe"];
27
28pub fn resolve_site_package_path(venv_path: &PathBuf) -> Result<PathBuf, BridgeError> {
37 if !venv_path.is_dir() {
39 return Err(BridgeError::VenvNotFound(venv_path.to_path_buf()));
40 }
41
42 #[cfg(windows)]
43 {
44 let site_packages = venv_path.join(PYTHON_LIB_DIR).join("site-packages");
45
46 if !site_packages.is_dir() {
48 return Err(BridgeError::Initialization(format!(
49 "unable to locate package directory: {}",
50 site_packages.display()
51 )));
52 }
53 Ok(site_packages)
54 }
55
56 #[cfg(not(windows))]
57 {
58 let lib_dir = venv_path.join(PYTHON_LIB_DIR);
59
60 if !lib_dir.is_dir() {
61 return Err(BridgeError::Initialization(format!(
62 "unable to locate lib directory: {}",
63 lib_dir.display()
64 )));
65 }
66
67 let python_version_dir = fs::read_dir(&lib_dir)
68 .map_err(|e| {
69 BridgeError::Initialization(format!("Failed to read lib directory: {}", e))
70 })?
71 .filter_map(|e| e.ok())
72 .find(|e| e.file_name().to_string_lossy().starts_with("python"))
73 .ok_or_else(|| {
74 BridgeError::Initialization("No python3.X directory found in venv/lib".to_string())
75 })?;
76
77 let site_packages = python_version_dir.path().join("site-packages");
78
79 if !site_packages.is_dir() {
80 return Err(BridgeError::Initialization(format!(
81 "unable to locate package directory: {}",
82 site_packages.display()
83 )));
84 }
85
86 Ok(site_packages)
87 }
88}
89
90pub fn resolve_python_path(venv_path: &PathBuf) -> Result<PathBuf, BridgeError> {
91 if !venv_path.is_dir() {
93 return Err(BridgeError::VenvNotFound(venv_path.to_path_buf()));
94 }
95
96 let bin_dir = venv_path.join(PYTHON_BIN_DIR);
97 if !bin_dir.is_dir() {
98 return Err(BridgeError::Initialization(format!(
99 "Python bin directory missing: {}",
100 bin_dir.display()
101 )));
102 }
103
104 for exe in PYTHON_EXE_CANDIDATES {
105 let candidate = bin_dir.join(exe);
106 if candidate.is_file() {
107 return Ok(candidate);
108 }
109 }
110
111 if let Ok(entries) = fs::read_dir(&bin_dir) {
112 if let Some(candidate) = entries.filter_map(|e| e.ok()).map(|e| e.path()).find(|p| {
113 p.file_name()
114 .and_then(|n| n.to_str())
115 .map(|name| name.contains("python"))
116 .unwrap_or(false)
117 && p.is_file()
118 }) {
119 return Ok(candidate);
120 }
121 }
122
123 Err(BridgeError::Initialization(format!(
124 "Path to python binary is not valid in {}",
125 venv_path.display()
126 )))
127}