1
votes

I'd like to create a command line that utilizes clap to parse input. The best I can come up with is a loop that asks the user for input, breaks it up with a regex and builds a Vec which it somehow passes to

loop {
    // Print command prompt and get command
    print!("> "); io::stdout().flush().expect("Couldn't flush stdout");

    let mut input = String::new(); // Take user input (to be parsed as clap args)
    io::stdin().read_line(&mut input).expect("Error reading input.");
    let args = WORD.captures_iter(&input)
           .map(|cap| cap.get(1).or(cap.get(2)).unwrap().as_str())
           .collect::<Vec<&str>>();

    let matches = App::new("MyApp")
        // ... Process Clap args/subcommands
    .get_matches(args); //match arguments from CLI args variable
}

Basically, I'm wondering if there is a way to direct Clap to use a pre-given list of arguments?

2
clap probably doesn't have that functionality because clap normally doesn't get the command line as a single string, but as a list of strings, already parsed by the shell. It is possible to ask clap to use a list of strings instead of using the programs' arguments. - mcarton
I am not sure if I understand the question properly. Are you asking how to parse all the args passed as one single string? - Shiva

2 Answers

2
votes

As @mcarton says, command line programs are passed their arguments as an array, rather than a string. The shell splits the original command line (taking into account quotes, variable expansion, etc).

If your requirements are simple, you could simply split your string on whitespace and pass that to Clap. Or, if you want to respect quoted strings, you could use shellwords to parse it:

let words = shellwords::split(input)?;
let matches = App::new("MyApp")
    // ... command line argument options
    .get_matches_from(words);
0
votes

This is how I ended up making the whole thing work:

First, I put my whole main function in a loop so that it'd be able to get commands and, well, stay in the CLI.

Next, I got input via stdin and split up the arguments

// Print command prompt and get command
print!("> ");
io::stdout().flush().expect("Couldn't flush stdout");
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Error reading input.");
let args = WORD.captures_iter(&input)
           .map(|cap| cap.get(1).or(cap.get(2)).unwrap().as_str())
           .collect::<Vec<&str>>();

I then used Clap to parse, sorta like how @harmic suggested

let matches = App::new("MyApp")
    // ... command line argument options
    .get_matches_from(words);

and used subcommands instead of arguments.

eg.

.subcommand(SubCommand::with_name("list")
    .help("Print namespaces currently tracked in the database."))

The whole file is here for the curious.