Reputation: 170
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?
Upvotes: 7
Views: 9382
Reputation: 1701
Rather than using parse
, you can use parse_from
which allows you to specify the iterator.
use clap::Parser;
#[derive(Debug, Parser, Default)]
#[command(about, version, no_binary_name(true))]
struct Cli {
#[arg(long, short, default_value_t = String::from("Default endpoint"))]
/// RPC endpoint of the node that this wallet will connect to
endpoint: String,
#[arg(long, short)]
refresh_rate: Option<u32>,
}
fn main() {
let input = vec!["--endpoint", "localhost:8000", "--refresh-rate", "15"];
let c = Cli::parse_from(input);
println!("{:?}", c);
}
Upvotes: 4
Reputation: 170
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.
Upvotes: 1
Reputation: 30597
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);
Upvotes: 7