intelli_shell/utils/
process.rs

1use std::{ops::Deref, process, sync::LazyLock};
2
3use sysinfo::{Pid, System};
4
5#[derive(Clone, Debug, PartialEq, Eq, strum::Display, strum::EnumString)]
6pub enum ShellType {
7    #[strum(serialize = "cmd", serialize = "cmd.exe")]
8    Cmd,
9    #[strum(serialize = "powershell", serialize = "powershell.exe")]
10    WindowsPowerShell,
11    #[strum(serialize = "pwsh", serialize = "pwsh.exe")]
12    PowerShellCore,
13    #[strum(to_string = "bash", serialize = "bash.exe")]
14    Bash,
15    #[strum(serialize = "sh")]
16    Sh,
17    #[strum(serialize = "fish")]
18    Fish,
19    #[strum(serialize = "zsh")]
20    Zsh,
21    #[strum(default, to_string = "{0}")]
22    Other(String),
23}
24
25static PARENT_SHELL: LazyLock<ShellType> = LazyLock::new(|| {
26    let parent_name = {
27        let pid = Pid::from_u32(process::id());
28
29        tracing::debug!("Retrieving info for pid {pid}");
30        let sys = System::new_all();
31
32        sys.process(Pid::from_u32(process::id()))
33            .expect("Couldn't retrieve current process from pid")
34            .parent()
35            .and_then(|parent_pid| sys.process(parent_pid))
36            .and_then(|parent_process| parent_process.name().to_str().map(|s| s.trim().to_lowercase()))
37    };
38
39    ShellType::try_from(
40        parent_name
41            .as_deref()
42            .inspect(|shell| tracing::info!("Detected shell: {shell}"))
43            .unwrap_or_else(|| {
44                let default = if cfg!(target_os = "windows") {
45                    "powershell"
46                } else {
47                    "sh"
48                };
49                tracing::warn!("Couldn't detect shell, assuming {default}");
50                default
51            }),
52    )
53    .expect("infallible")
54});
55
56/// Retrieves the current shell
57pub fn get_shell() -> &'static ShellType {
58    PARENT_SHELL.deref()
59}
60
61static WORING_DIR: LazyLock<String> = LazyLock::new(|| {
62    std::env::current_dir()
63        .inspect_err(|err| tracing::warn!("Couldn't retrieve current dir: {err}"))
64        .ok()
65        .and_then(|p| p.to_str().map(|s| s.to_owned()))
66        .unwrap_or_default()
67});
68
69/// Retrieves the working directory
70pub fn get_working_dir() -> &'static str {
71    WORING_DIR.deref()
72}
73
74/// Formats an env var name into its shell representation, based on the current shell
75pub fn format_env_var(var: impl AsRef<str>) -> String {
76    let var = var.as_ref();
77    match get_shell() {
78        ShellType::Cmd => format!("%{var}%"),
79        ShellType::WindowsPowerShell | ShellType::PowerShellCore => format!("$env:{var}"),
80        _ => format!("${var}"),
81    }
82}