bare-script 0.1.1

The type-safe scripting authority for Rust. A framework for building robust shell commands and automation with 'Parse, don't validate' philosophy.
Documentation
//! Shell integration helpers.
//!
//! This module provides helper functions for common shell operations.

use crate::error::ScriptResult;
use crate::sync::CommandBuilder;

/// Finds the full path to an executable.
///
/// # Errors
///
/// Returns an error if the executable is not found.
pub fn which(name: &str) -> ScriptResult<Option<std::path::PathBuf>> {
    #[cfg(windows)]
    {
        let result = CommandBuilder::new("where")
            .arg(name)
            .capture_output()
            .execute()?;

        if result.success() {
            let path = result
                .stdout_str()
                .lines()
                .next()
                .map(std::path::PathBuf::from);
            Ok(path)
        } else {
            Ok(None)
        }
    }

    #[cfg(not(windows))]
    {
        let result = CommandBuilder::new("which")
            .arg(name)
            .capture_output()
            .execute()?;

        if result.success() {
            let path = result
                .stdout_str()
                .trim()
                .lines()
                .next()
                .map(std::path::PathBuf::from);
            Ok(path)
        } else {
            Ok(None)
        }
    }
}

/// Checks if an executable exists in PATH.
///
/// # Example
///
/// ```rust
/// use bare_script::shell::command_exists;
///
/// assert!(command_exists("ls").unwrap_or(false) || command_exists("dir").unwrap_or(false));
/// ```
pub fn command_exists(name: &str) -> ScriptResult<bool> {
    Ok(which(name)?.is_some())
}

/// Gets the current platform's shell executable name.
///
/// Returns "cmd" on Windows, "sh" on Unix.
pub fn default_shell() -> &'static str {
    #[cfg(windows)]
    {
        "cmd"
    }

    #[cfg(not(windows))]
    {
        "sh"
    }
}

/// Gets the shell flag for executing a command string.
///
/// Returns "/C" on Windows, "-c" on Unix.
pub fn shell_exec_flag() -> &'static str {
    #[cfg(windows)]
    {
        "/C"
    }

    #[cfg(not(windows))]
    {
        "-c"
    }
}

/// Executes a shell command string.
///
/// # Errors
///
/// Returns an error if the shell command fails.
pub fn eval(command: &str) -> ScriptResult<crate::output::Output> {
    CommandBuilder::new(default_shell())
        .arg(shell_exec_flag())
        .arg(command)
        .capture_output()
        .execute()
}

/// Gets environment variable value safely.
///
/// Returns None if the variable is not set.
pub fn get_env(var: &str) -> Option<std::ffi::OsString> {
    std::env::var(var).ok().map(std::ffi::OsString::from)
}

/// Checks if running on Windows.
pub fn is_windows() -> bool {
    cfg!(windows)
}

/// Checks if running on a Unix-like system.
pub fn is_unix() -> bool {
    cfg!(unix)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_default_shell() {
        let shell = default_shell();
        assert!(!shell.is_empty());
    }

    #[test]
    fn test_shell_exec_flag() {
        let flag = shell_exec_flag();
        assert!(!flag.is_empty());
    }

    #[test]
    fn test_platform_checks() {
        let is_win = is_windows();
        let is_nix = is_unix();
        assert!(is_win || is_nix);
        assert!(!(is_win && is_nix));
    }
}