guartcl 0.5.0-beta2

Enhanced Jim Tcl.
Documentation
//! Code to integrate Usage CLI parsing.

use std::path::Path;

use jimtcl::{Interp, JimError, JimObject, JimResult};
use usage::{
    Parser, Spec,
    parse::{ParseOutput, ParseValue},
};

/// Parse command-line arguments using the usage spec in the script, saving the
/// results in the `usage` variable.
pub fn parse_args(interp: &Interp, script: &Path, args: &[String]) -> JimResult<()> {
    let spec = Spec::parse_script(script).map_err(JimError::wrap)?;
    let parsed = Parser::new(&spec).parse(args);
    match parsed {
        Err(e) => {
            eprintln!("{}", e);
            Err(JimError::from("command line parse failed"))
        }
        Ok(args) => add_args(interp, &args),
    }
}

/// Add the parsed arguments to a `usage` variable.
///
/// This function is similar to [ParseOutput::as_env], except it uses Tcl lists
/// instead of shell quoting.
fn add_args(interp: &Interp, args: &ParseOutput) -> JimResult<()> {
    let usage = JimObject::empty(interp);
    for (flag, val) in &args.flags {
        add_arg(interp, &usage, &flag.name, val)?;
    }
    for (arg, val) in &args.args {
        add_arg(interp, &usage, &arg.name, val)?;
    }
    // make sure unset flags are set
    for (_, spec) in &args.available_flags {
        if spec.arg.is_none() && !usage.dict_exists(&spec.name)? {
            usage.dict_set(&spec.name, 0)?;
        }
    }
    interp.set_variable("usage", usage)?;

    Ok(())
}

fn add_arg<'jim>(
    interp: &'jim Interp,
    usage: &JimObject<'jim>,
    name: &str,
    value: &ParseValue,
) -> JimResult<()> {
    match value {
        ParseValue::String(s) => usage.dict_set(name, s.as_str())?,
        ParseValue::Bool(true) => usage.dict_set(name, "yes")?,
        ParseValue::Bool(false) => usage.dict_set(name, "no")?,
        ParseValue::MultiBool(bools) => {
            let n = bools.iter().filter(|b| **b).count();
            usage.dict_set(name, n)?;
        }
        ParseValue::MultiString(strings) => {
            let val = JimObject::empty(interp);
            for v in strings {
                val.list_append(v.as_str());
            }
            usage.dict_set_obj(interp.new_string(name), val)?;
        }
    }
    Ok(())
}