parallax_runtime/
discover.rs1use std::path::{Path, PathBuf};
4use std::process::Command;
5
6#[derive(Clone, Debug)]
8pub struct DiscoveredBinary {
9 pub path: PathBuf,
11 pub version: Option<String>,
13}
14
15pub fn discover_python() -> Option<DiscoveredBinary> {
20 let mut candidates: Vec<PathBuf> = vec![
21 PathBuf::from("python"),
22 PathBuf::from("python3"),
23 PathBuf::from("py"),
24 ];
25
26 if let Some(local) = std::env::var_os("LOCALAPPDATA") {
27 let base = PathBuf::from(local).join("Programs").join("Python");
28 if let Ok(entries) = std::fs::read_dir(&base) {
29 for ent in entries.flatten() {
30 let p = ent.path().join("python.exe");
31 if p.is_file() {
32 candidates.push(p);
33 }
34 }
35 }
36 }
37 for pf in [
39 std::env::var_os("ProgramFiles"),
40 std::env::var_os("ProgramFiles(x86)"),
41 ]
42 .into_iter()
43 .flatten()
44 {
45 let base = PathBuf::from(pf).join("Python");
46 if let Ok(entries) = std::fs::read_dir(&base) {
47 for ent in entries.flatten() {
48 let p = ent.path().join("python.exe");
49 if p.is_file() {
50 candidates.push(p);
51 }
52 }
53 }
54 }
55
56 for cand in candidates {
57 if let Some(bin) = probe_python(&cand) {
58 return Some(bin);
59 }
60 }
61 None
62}
63
64fn probe_python(cmd: &Path) -> Option<DiscoveredBinary> {
65 let output = Command::new(cmd)
66 .args(["-c", "import sys; print(sys.version.split()[0])"])
67 .output()
68 .ok()?;
69 if !output.status.success() {
70 return None;
71 }
72 let version = String::from_utf8_lossy(&output.stdout).trim().to_string();
73 if version.is_empty() {
74 return None;
75 }
76 let stderr = String::from_utf8_lossy(&output.stderr);
78 if stderr.contains("Microsoft Store") {
79 return None;
80 }
81 Some(DiscoveredBinary {
82 path: cmd.to_path_buf(),
83 version: Some(version),
84 })
85}
86
87pub fn discover_javascript() -> Option<DiscoveredBinary> {
89 let candidates = [PathBuf::from("node"), PathBuf::from("nodejs")];
90 for cand in candidates {
91 if let Some(bin) = probe_node(&cand) {
92 return Some(bin);
93 }
94 }
95 let pf = std::env::var_os("ProgramFiles").map(PathBuf::from);
97 if let Some(pf) = pf {
98 let p = pf.join("nodejs").join("node.exe");
99 if let Some(bin) = probe_node(&p) {
100 return Some(bin);
101 }
102 }
103 None
104}
105
106fn probe_node(cmd: &Path) -> Option<DiscoveredBinary> {
107 let output = Command::new(cmd).arg("-v").output().ok()?;
108 if !output.status.success() {
109 return None;
110 }
111 let version = String::from_utf8_lossy(&output.stdout)
112 .trim()
113 .trim_start_matches('v')
114 .to_string();
115 if version.is_empty() {
116 return None;
117 }
118 Some(DiscoveredBinary {
119 path: cmd.to_path_buf(),
120 version: Some(version),
121 })
122}