Interpret `--` like `cargo run` using clap

174 Views Asked by At

In Rust, how can I implement argument parsing like what cargo does here?

cargo run -v -- -w

-v is a cargo option, but -w becomes an option on the executable cargo builds.

I would like my software to do the same thing. I.e. my user provides options to my executable before --, but then any arguments after -- forward to a different executable that my executable invokes.

Is this possible using clap?

2

There are 2 best solutions below

0
On

This is possible using the Arg::raw() method.

Using the derive syntax:

use clap::Parser;

#[derive(Parser, Debug)]
struct Args {
    #[arg(raw = true)]
    cmd: Vec<String>,
}
0
On

It should work out of the box:

use clap::*;

fn main() {
    let cmd = Command::new ("prog")
        .arg (Arg::new ("verbose")
              .short ('v'))
        .arg (Arg::new("extra")
              .num_args (0..));
    
    let m = cmd.get_matches_from(vec![ "prog", "-v", "1", "--", "-w" ]);
    println!("verbose: {}", m.get_one::<String>("verbose").unwrap());
    println!("extra: {:?}", m.get_many::<String>("extra").unwrap().collect::<Vec<_>>());
}

Playground

Or with the derive API:

use clap::Parser;

#[derive (Parser, Debug)]
struct Args {
    #[arg(short, long)]
    verbose: bool,
    extra: Vec<String>,
}

fn main() {
    let args = Args::parse_from(vec![ "prog", "-v", "--", "-w" ]);
    println!("{:?}", args);
}

Playground

If you want the -- to be mandatory even if the extra arguments don't start with -, you need to use last for your catch-all argument:

.arg (Arg::new("extra")
      .num_args (0..)
      .last(true)

or

#[derive (Parser, Debug)]
struct Args {
    #[arg(short, long)]
    verbose: bool,
    #[arg(last = true)]
    extra: Vec<String>,
}