command-stream 1.11.0

Modern shell command execution library with streaming, async iteration, and event support
Documentation
//! Shared options for the virtual command and the native CLI.
use super::SourceOptions;

pub const USAGE: &str = "$fy - Convert shell scripts and competitor source to command-stream modules\n\nUsage: $fy [options] [input.sh|input.mjs|input.cjs|-] [output.mjs]\nRead stdin when no input is given or input is -; print unless output is given.\n\n  --from shell|javascript|zx|execa|shelljs|cross-spawn|bun\n  --shell sh|bash|dash|zsh    Interpreter (default: shebang or sh)\n  --module NAME             Target package (default: command-stream)\n  --no-use-m                Import the installed package directly\n  --no-shebang              Omit the generated interpreter line\n  --commonjs                Wrap CommonJS source as an ES module\n  --structural              Compile the supported shell subset to JS statements\n  -h, --help                Show this help\n\nShell programs use command-stream + use-m and retain shell semantics.\nJavaScript imports migrate to command-stream compatibility APIs.\nOutput files must not already exist.\n";

#[derive(Default)]
pub struct Arguments {
    pub options: SourceOptions,
    pub positional: Vec<String>,
    pub structural: bool,
    pub help: bool,
}

pub fn parse_arguments(args: &[String]) -> Result<Arguments, String> {
    let mut result = Arguments::default();
    let mut args = args.iter();
    while let Some(arg) = args.next() {
        match arg.as_str() {
            "--" => {
                result.positional.extend(args.cloned());
                break;
            }
            "--from" | "--shell" | "--module" => {
                let value = args
                    .next()
                    .filter(|value| !value.starts_with("--") && !value.is_empty())
                    .ok_or_else(|| format!("Missing value for {arg}"))?;
                match arg.as_str() {
                    "--from" => result.options.from.clone_from(value),
                    "--shell" => result.options.shell = Some(value.clone()),
                    _ => result.options.module_name.clone_from(value),
                }
            }
            "--no-use-m" => result.options.use_m = false,
            "--no-shebang" => result.options.shebang = false,
            "--commonjs" => result.options.commonjs = true,
            "--structural" => result.structural = true,
            "--help" | "-h" => result.help = true,
            "-" => result.positional.push(arg.clone()),
            value if value.starts_with('-') => return Err(format!("Unknown option: {value}")),
            _ => result.positional.push(arg.clone()),
        }
    }
    if result.positional.len() > 2 {
        return Err("Expected input and optional output filename".into());
    }
    result.options.filename = result.positional.first().cloned().unwrap_or_default();
    Ok(result)
}