argc/runtime/
mod.rs

1#[cfg(feature = "native-runtime")]
2pub mod navite;
3
4use anyhow::Result;
5use std::{collections::HashMap, env};
6
7pub trait Runtime
8where
9    Self: Copy + Clone,
10{
11    const INTERNAL_SYMBOL: &'static str = "___internal___";
12    fn os(&self) -> String;
13    fn shell_path(&self) -> Result<String>;
14    fn bash_path(&self) -> Option<String>;
15    fn exec_bash_functions(
16        &self,
17        script_file: &str,
18        functions: &[&str],
19        args: &[String],
20        envs: HashMap<String, String>,
21    ) -> Option<Vec<String>>;
22    fn current_exe(&self) -> Option<String>;
23    fn current_dir(&self) -> Option<String>;
24    fn env_vars(&self) -> HashMap<String, String>;
25    fn env_var(&self, name: &str) -> Option<String>;
26    fn which(&self, name: &str) -> Option<String>;
27    fn exist_path(&self, path: &str) -> bool;
28    fn parent_path(&self, path: &str) -> Option<String>;
29    fn join_path(&self, path: &str, parts: &[&str]) -> String;
30    fn chdir(&self, cwd: &str, cd: &str) -> Option<String>;
31    fn metadata(&self, path: &str) -> Option<(bool, bool, bool)>;
32    fn read_dir(&self, path: &str) -> Option<Vec<String>>;
33    fn read_to_string(&self, path: &str) -> Option<String>;
34
35    fn is_windows(&self) -> bool {
36        self.os() == "windows"
37    }
38
39    fn shell_args(&self, shell_path: &str) -> Vec<String> {
40        if let Some(name) = self.basename(shell_path).map(|v| v.to_lowercase()) {
41            match name.as_str() {
42                "bash" => vec!["--noprofile".to_string(), "--norc".to_string()],
43                _ => vec![],
44            }
45        } else {
46            vec![]
47        }
48    }
49
50    fn basename(&self, path: &str) -> Option<String> {
51        let parts: Vec<_> = path.split(['/', '\\']).collect();
52        let last_part = parts.last()?;
53        let name = match last_part.rsplit_once('.') {
54            Some((v, _)) => v.to_string(),
55            None => last_part.to_string(),
56        };
57        Some(name)
58    }
59
60    fn load_dotenv(&self, path: &str) -> Option<HashMap<String, String>> {
61        let contents = self.read_to_string(path)?;
62        let mut output = HashMap::new();
63        for line in contents.lines() {
64            if line.starts_with('#') || line.trim().is_empty() {
65                continue;
66            }
67            if let Some((key, value)) = line.split_once('=') {
68                let env_name = key.trim().to_string();
69                let env_value = value.trim().to_string();
70                let env_value = if (env_value.starts_with('"') && env_value.ends_with('"'))
71                    || (env_value.starts_with('\'') && env_value.ends_with('\''))
72                {
73                    &env_value[1..env_value.len() - 1]
74                } else {
75                    &env_value
76                };
77
78                if env::var(&env_name).is_err() {
79                    output.insert(env_name, env_value.to_string());
80                }
81            }
82        }
83        Some(output)
84    }
85
86    fn path_env_with_current_exe(&self) -> String {
87        let mut path_env = self.env_var("PATH").unwrap_or_default();
88        if let Some(exe_dir) = self
89            .current_exe()
90            .and_then(|exe_path| self.parent_path(&exe_path))
91        {
92            if self.is_windows() {
93                path_env = format!("{exe_dir};{path_env}")
94            } else {
95                path_env = format!("{exe_dir}:{path_env}")
96            }
97        }
98        path_env
99    }
100}