redskull_lib/
runtime_deps.rs1pub struct RuntimeHint {
7 pub package: &'static str,
9 pub reason: String,
11}
12
13pub fn detect_runtime_hints(file_paths: &[String]) -> Vec<RuntimeHint> {
16 let mut hints = Vec::new();
17
18 let r_scripts: Vec<&str> = file_paths
19 .iter()
20 .filter(|p| {
21 let lower = p.to_lowercase();
22 lower.ends_with(".r") || lower.ends_with(".rmd") || lower.ends_with(".rscript")
23 })
24 .map(|p| p.as_str())
25 .collect();
26
27 let py_scripts: Vec<&str> = file_paths
28 .iter()
29 .filter(|p| {
30 let lower = p.to_lowercase();
31 lower.ends_with(".py") && !lower.contains("setup.py") && !lower.contains("conf.py")
32 })
33 .map(|p| p.as_str())
34 .collect();
35
36 let has_r_description =
37 file_paths.iter().any(|p| p == "DESCRIPTION" || p.ends_with("/DESCRIPTION"));
38 let has_renv = file_paths.iter().any(|p| p.ends_with("renv.lock"));
39 let has_requirements_txt = file_paths.iter().any(|p| {
40 let name = p.rsplit('/').next().unwrap_or(p);
41 name == "requirements.txt"
42 });
43
44 if !r_scripts.is_empty() || has_r_description || has_renv {
45 let count = r_scripts.len();
46 let examples: Vec<&str> = r_scripts.iter().take(3).copied().collect();
47 let detail = if count > 0 {
48 format!("found {count} R script(s): {}", examples.join(", "))
49 } else if has_r_description {
50 "found DESCRIPTION file (R package)".to_string()
51 } else {
52 "found renv.lock file".to_string()
53 };
54 hints.push(RuntimeHint { package: "r-base", reason: detail });
55 }
56
57 if !py_scripts.is_empty() || has_requirements_txt {
58 let count = py_scripts.len();
59 let examples: Vec<&str> = py_scripts.iter().take(3).copied().collect();
60 let detail = if count > 0 {
61 format!("found {count} Python script(s): {}", examples.join(", "))
62 } else {
63 "found requirements.txt file".to_string()
64 };
65 hints.push(RuntimeHint { package: "python", reason: detail });
66 }
67
68 hints
69}