vv-agent 0.6.2

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
use std::path::Path;

use super::super::path::{expand_home_path, looks_like_path};
use super::super::platform::{infer_shell_kind, invocation_from_executable, normalize_shell_name};
use super::super::ShellInvocation;
use super::discovery::WindowsShellDiscovery;
use super::priority::normalize_windows_priority;
use super::programs::{resolve_windows_git_bash, resolve_windows_powershell};

pub(super) fn resolve_windows_shell_with_discovery(
    shell: Option<&str>,
    windows_shell_priority: Option<&[String]>,
    discovery: &impl WindowsShellDiscovery,
) -> Result<ShellInvocation, String> {
    let selected_shell = shell.unwrap_or_default().trim();
    if !selected_shell.is_empty() && normalize_shell_name(selected_shell) != "auto" {
        return resolve_windows_shell_entry(selected_shell, discovery).ok_or_else(|| {
            format!("Configured shell is unavailable on Windows: {selected_shell}")
        });
    }

    for entry in normalize_windows_priority(windows_shell_priority) {
        if let Some(invocation) = resolve_windows_shell_entry(&entry, discovery) {
            return Ok(invocation);
        }
    }

    resolve_windows_shell_entry("cmd", discovery)
        .ok_or_else(|| "Unable to resolve Windows command shell.".to_string())
}

fn resolve_windows_shell_entry(
    entry: &str,
    discovery: &impl WindowsShellDiscovery,
) -> Option<ShellInvocation> {
    let shell_name = normalize_shell_name(entry);
    if matches!(shell_name.as_str(), "cmd" | "cmd.exe") {
        let executable = discovery
            .env_var("COMSPEC")
            .or_else(|| discovery.which("cmd.exe"))
            .unwrap_or_else(|| "cmd.exe".to_string());
        return Some(invocation_from_executable("cmd", executable));
    }

    if matches!(shell_name.as_str(), "powershell" | "powershell.exe") {
        return resolve_windows_powershell(discovery)
            .map(|executable| invocation_from_executable("powershell", executable));
    }

    if matches!(shell_name.as_str(), "pwsh" | "pwsh.exe") {
        return discovery
            .which("pwsh.exe")
            .or_else(|| discovery.which("pwsh"))
            .map(|executable| invocation_from_executable("pwsh", executable));
    }

    if matches!(shell_name.as_str(), "git-bash" | "gitbash") {
        return resolve_windows_git_bash(discovery)
            .map(|executable| invocation_from_executable("bash", executable));
    }

    if matches!(shell_name.as_str(), "bash" | "bash.exe") {
        return discovery
            .which("bash.exe")
            .or_else(|| discovery.which("bash"))
            .or_else(|| resolve_windows_git_bash(discovery))
            .map(|executable| invocation_from_executable("bash", executable));
    }

    if shell_name.is_empty() {
        return None;
    }

    if looks_like_path(entry) {
        let candidate = expand_home_path(entry);
        if !discovery.is_file(&candidate) {
            return None;
        }
        let inferred = infer_shell_kind(
            candidate
                .file_name()
                .and_then(|name| name.to_str())
                .unwrap_or(entry),
        );
        return Some(invocation_from_executable(
            inferred,
            candidate.to_string_lossy().to_string(),
        ));
    }

    discovery.which(entry).map(|executable| {
        let inferred = infer_shell_kind(
            Path::new(&executable)
                .file_name()
                .and_then(|name| name.to_str())
                .unwrap_or(&executable),
        );
        invocation_from_executable(inferred, executable)
    })
}