gram-data 0.3.11

Unified gram CLI and library for validating gram notation
Documentation
use std::process::Command;

pub fn run(args: &[String]) -> i32 {
    if args.is_empty() {
        eprintln!(
            "error: no sub-command given\n\n\
             Try 'gram --help' for a list of built-in commands."
        );
        return 2;
    }

    let subcommand = &args[0];
    let binary = format!("gram-{subcommand}");

    if let Some(path) = find_on_path(&binary) {
        let remaining = &args[1..];

        #[cfg(unix)]
        {
            use std::os::unix::process::CommandExt;
            let err = Command::new(&path).args(remaining).exec();
            eprintln!("error: failed to exec {}: {err}", path.display());
            return 2;
        }

        #[cfg(not(unix))]
        match Command::new(&path).args(remaining).status() {
            Ok(status) => return status.code().unwrap_or(1),
            Err(e) => {
                eprintln!("error: failed to exec {}: {e}", path.display());
                return 2;
            }
        }
    }

    eprintln!(
        "error: unknown sub-command '{subcommand}'\n\n\
         No built-in sub-command '{subcommand}' and no 'gram-{subcommand}' binary found on PATH.\n\n\
         Try 'gram --help' for a list of built-in commands."
    );
    2
}

fn find_on_path(binary: &str) -> Option<std::path::PathBuf> {
    let path_var = std::env::var_os("PATH")?;
    for dir in std::env::split_paths(&path_var) {
        #[cfg(windows)]
        let candidate = dir.join(format!("{binary}.exe"));
        #[cfg(not(windows))]
        let candidate = dir.join(binary);

        if candidate.is_file() {
            #[cfg(unix)]
            {
                use std::os::unix::fs::PermissionsExt;
                if let Ok(meta) = std::fs::metadata(&candidate) {
                    if meta.permissions().mode() & 0o111 != 0 {
                        return Some(candidate);
                    }
                }
            }
            #[cfg(not(unix))]
            return Some(candidate);
        }
    }
    None
}