Skip to main content

mangofetch_core/core/
process.rs

1fn enhanced_path() -> Option<&'static str> {
2    use std::sync::OnceLock;
3    static CACHED: OnceLock<Option<String>> = OnceLock::new();
4    CACHED
5        .get_or_init(|| {
6            let bin_dir = crate::core::paths::app_data_dir()?.join("bin");
7            let sep = if cfg!(windows) { ";" } else { ":" };
8            let current = std::env::var("PATH").unwrap_or_default();
9
10            #[allow(unused_mut)]
11            let mut extra_dirs: Vec<String> = vec![bin_dir.display().to_string()];
12
13            #[cfg(target_os = "macos")]
14            {
15                extra_dirs.push("/opt/homebrew/bin".into());
16                extra_dirs.push("/usr/local/bin".into());
17            }
18
19            #[cfg(target_os = "linux")]
20            {
21                if let Some(home) = dirs::home_dir() {
22                    extra_dirs.push(home.join(".local").join("bin").display().to_string());
23                }
24                extra_dirs.push("/usr/local/bin".into());
25            }
26
27            Some(format!("{}{}{}", extra_dirs.join(sep), sep, current))
28        })
29        .as_deref()
30}
31
32pub fn command<S: AsRef<std::ffi::OsStr>>(program: S) -> tokio::process::Command {
33    let mut cmd = tokio::process::Command::new(program);
34    #[cfg(target_os = "windows")]
35    cmd.creation_flags(0x08000000);
36    if let Some(path) = enhanced_path() {
37        cmd.env("PATH", path);
38    }
39    cmd.env_remove("PYTHONHOME");
40    cmd.env_remove("PYTHONPATH");
41    cmd.env("PYTHONIOENCODING", "utf-8");
42    cmd.env("PYTHONUTF8", "1");
43    cmd.stdin(std::process::Stdio::null());
44    cmd
45}
46
47pub fn std_command<S: AsRef<std::ffi::OsStr>>(program: S) -> std::process::Command {
48    let mut cmd = std::process::Command::new(program);
49    #[cfg(target_os = "windows")]
50    {
51        use std::os::windows::process::CommandExt;
52        cmd.creation_flags(0x08000000);
53    }
54    if let Some(path) = enhanced_path() {
55        cmd.env("PATH", path);
56    }
57    cmd.env_remove("PYTHONHOME");
58    cmd.env_remove("PYTHONPATH");
59    cmd.env("PYTHONIOENCODING", "utf-8");
60    cmd.env("PYTHONUTF8", "1");
61    cmd.stdin(std::process::Stdio::null());
62    cmd
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use std::ffi::OsStr;
69
70    #[test]
71    fn test_tokio_command_construction() {
72        let cmd = command("test_prog");
73        let std_cmd = cmd.as_std();
74
75        assert_eq!(std_cmd.get_program(), OsStr::new("test_prog"));
76
77        let mut has_python_io = false;
78        let mut has_python_utf8 = false;
79        let mut removed_pythonhome = false;
80        let mut removed_pythonpath = false;
81
82        for (k, v) in std_cmd.get_envs() {
83            if k == OsStr::new("PYTHONIOENCODING") {
84                assert_eq!(v, Some(OsStr::new("utf-8")));
85                has_python_io = true;
86            }
87            if k == OsStr::new("PYTHONUTF8") {
88                assert_eq!(v, Some(OsStr::new("1")));
89                has_python_utf8 = true;
90            }
91            if k == OsStr::new("PYTHONHOME") {
92                assert_eq!(v, None);
93                removed_pythonhome = true;
94            }
95            if k == OsStr::new("PYTHONPATH") {
96                assert_eq!(v, None);
97                removed_pythonpath = true;
98            }
99        }
100
101        assert!(has_python_io, "PYTHONIOENCODING was not set");
102        assert!(has_python_utf8, "PYTHONUTF8 was not set");
103        assert!(removed_pythonhome, "PYTHONHOME was not removed");
104        assert!(removed_pythonpath, "PYTHONPATH was not removed");
105    }
106
107    #[test]
108    fn test_std_command_construction() {
109        let std_cmd = std_command("test_prog");
110
111        assert_eq!(std_cmd.get_program(), OsStr::new("test_prog"));
112
113        let mut has_python_io = false;
114        let mut has_python_utf8 = false;
115        let mut removed_pythonhome = false;
116        let mut removed_pythonpath = false;
117
118        for (k, v) in std_cmd.get_envs() {
119            if k == OsStr::new("PYTHONIOENCODING") {
120                assert_eq!(v, Some(OsStr::new("utf-8")));
121                has_python_io = true;
122            }
123            if k == OsStr::new("PYTHONUTF8") {
124                assert_eq!(v, Some(OsStr::new("1")));
125                has_python_utf8 = true;
126            }
127            if k == OsStr::new("PYTHONHOME") {
128                assert_eq!(v, None);
129                removed_pythonhome = true;
130            }
131            if k == OsStr::new("PYTHONPATH") {
132                assert_eq!(v, None);
133                removed_pythonpath = true;
134            }
135        }
136
137        assert!(has_python_io, "PYTHONIOENCODING was not set");
138        assert!(has_python_utf8, "PYTHONUTF8 was not set");
139        assert!(removed_pythonhome, "PYTHONHOME was not removed");
140        assert!(removed_pythonpath, "PYTHONPATH was not removed");
141    }
142}