fnm 1.39.0

Fast and simple Node.js version manager
#![cfg(not(unix))]

use crate::shell::Shell;
use log::{debug, warn};
use sysinfo::{ProcessRefreshKind, System, UpdateKind};

pub fn infer_shell() -> Option<Box<dyn Shell>> {
    let mut system = System::new();
    let mut current_pid = sysinfo::get_current_pid().ok();

    system
        .refresh_processes_specifics(ProcessRefreshKind::new().with_exe(UpdateKind::OnlyIfNotSet));

    while let Some(pid) = current_pid {
        if let Some(process) = system.process(pid) {
            current_pid = process.parent();
            debug!("pid {pid} parent process is {current_pid:?}");
            let process_name = process
                .exe()
                .and_then(|x| {
                    tap_none(x.file_stem(), || {
                        warn!("failed to get file stem from {:?}", x);
                    })
                })
                .and_then(|x| {
                    tap_none(x.to_str(), || {
                        warn!("failed to convert file stem to string: {:?}", x);
                    })
                })
                .map(str::to_lowercase);
            if let Some(shell) = process_name
                .as_ref()
                .map(|x| &x[..])
                .and_then(super::shell_from_string)
            {
                return Some(shell);
            }
        } else {
            warn!("process not found for {pid}");
            current_pid = None;
        }
    }

    None
}

fn tap_none<T, F>(opt: Option<T>, f: F) -> Option<T>
where
    F: FnOnce(),
{
    match &opt {
        Some(_) => (),
        None => f(),
    };

    opt
}