py_executer_lib/
py_executer_lib.rs

1pub mod macros;
2
3use anyhow::anyhow;
4use colored::*;
5use std::collections::HashMap;
6use std::process::Command;
7use std::{env, path::PathBuf};
8
9/// Appends the current directory to PYTHONPATH if it is valid.
10fn append_pwd_to_pythonpath(current_dir: PathBuf) -> HashMap<String, String> {
11    if !current_dir.exists() {
12        error_println!(
13            "Current directory not valid: {}",
14            current_dir.display().to_string().bold()
15        );
16        HashMap::new()
17    } else {
18        let mut path = env::var("PYTHONPATH").unwrap_or_default();
19        if !path.contains(&current_dir.to_string_lossy().to_string()) {
20            if !path.is_empty() {
21                path.push(':');
22            }
23            path.push_str(current_dir.to_string_lossy().to_string().as_str());
24            return HashMap::from([("PYTHONPATH".to_string(), path)]);
25        }
26        HashMap::new()
27    }
28}
29
30/// Processes additional environment variables from CLI arguments.
31pub fn set_additional_env_var(
32    additional_env_from_args: Vec<String>,
33    quiet: bool,
34) -> HashMap<String, String> {
35    let mut additional_env = HashMap::new();
36
37    //add current dir to PYTHONPATH
38    let current_dir = env::current_dir().unwrap();
39    additional_env.extend(append_pwd_to_pythonpath(current_dir));
40
41    for env_var in additional_env_from_args {
42        if let Some(pos) = env_var.find('=') {
43            let key = env_var[..pos].to_string();
44            let value = env_var[pos + 1..].to_string();
45            additional_env.insert(key.clone(), value.clone());
46            if !quiet {
47                println!("Setting env: {} = {}", key.bold(), value);
48            }
49        } else {
50            if !quiet {
51                warning_println!(
52                    "Warning: Ignoring malformed environment variable: {}",
53                    env_var.bold()
54                );
55            }
56        }
57    }
58    additional_env
59}
60
61/// Parse and validate a script path.
62///
63/// The function takes a `PathBuf` as argument and checks if the path exists.
64/// If the path exists, it returns a tuple of `(PathBuf, PathBuf)`, where the first element
65/// is the absolute path of the script and the second element is the parent directory of
66/// the script.
67///
68/// # Errors
69///
70/// The function returns an `Err` if the path does not exist or if the parent directory
71/// cannot be obtained.
72pub fn validate_to_absolute_path(script_path: &PathBuf) -> anyhow::Result<PathBuf> {
73    match script_path.canonicalize() {
74        Ok(path) => {
75            if !path.exists() {
76                return Err(anyhow!("{} not exists", path.display().to_string().bold()));
77            }
78            Ok(path)
79        }
80        Err(err) => Err(anyhow!("Failed to get absolute path of script: {}", err)),
81    }
82}
83
84pub fn get_uv_path() -> anyhow::Result<String> {
85    // For Unix-like systems (Linux, macOS)
86    #[cfg(not(target_os = "windows"))]
87    let find_executable = "which";
88
89    // For Windows
90    #[cfg(target_os = "windows")]
91    let find_executable = "where";
92
93    let output = Command::new(find_executable).arg("uv").output()?;
94    if output.status.success() {
95        // found uv
96        let path = String::from_utf8(output.stdout)?.trim().to_string();
97        Ok(path)
98    } else {
99        // not found uv, hint to install it
100
101        // for unix, run wget -qO- https://astral.sh/uv/install.sh | sh
102        eprintln!("Please run the following command to install uv:");
103        #[cfg(not(target_os = "windows"))]
104        eprintln!("wget -qO- https://astral.sh/uv/install.sh | sh");
105
106        // for windows, run powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
107        #[cfg(target_os = "windows")]
108        eprintln!(
109            "powershell -ExecutionPolicy ByPass -c \"irm https://astral.sh/uv/install.ps1 | iex\""
110        );
111        Err(anyhow!("uv not installed"))
112    }
113}
114
115pub fn get_python_exec_path(venv_path: &PathBuf) -> PathBuf {
116    PathBuf::from(if cfg!(target_os = "windows") {
117        venv_path
118            .join("Scripts")
119            .join("python.exe")
120            .to_string_lossy()
121            .to_string()
122    } else {
123        venv_path
124            .join("bin")
125            .join("python")
126            .to_string_lossy()
127            .to_string()
128    })
129}
130
131#[test]
132fn test_validate_to_absolute_path() {
133    let script_path = PathBuf::from("test.py");
134    let result = validate_to_absolute_path(&script_path);
135    assert!(result.is_ok());
136    println!("Script path: {}", result.unwrap().display().to_string());
137
138    let non_existent_path = PathBuf::from("");
139    let result = validate_to_absolute_path(&non_existent_path);
140    assert!(result.is_err());
141    println!("Error: {}", result.unwrap_err());
142}