Clap: how can I handle variable number of positional arguments like cp/mv etc.?

I want to create an utility that accepts positional arguments like some Linux utilities that accept a variable number of SRC arguments followed by a single DEST argument, with a syntax that is analog to 'cp'.

I couldn't find a way to do that. The closest I got are these:

struct CmdArgs {

    /// Source location
    #[arg(required=true)]
    source: Vec<String>,

    /// Destination location
    #[arg(required=true)]
    dest: String,
}

but this one doesn't allow to make remaining arguments as positional with --, like

util -- a -x

doesn't work. It is supposed to accept -x as DEST argument, but fails:

error: unexpected argument '-x' found

Usage: util [OPTIONS] <SOURCE>... <DEST>

For more information, try '--help'.

The other option would be just:

struct CmdArgs {

    /// Source location
    #[arg(required=true)]
    source: Vec<String>,

This gives me the right behavior (with having to split the dest argument output source manually), but the help message will be wrong/misleading then:

Usage: util [OPTIONS] <SOURCE>...

Arguments:
  <SOURCE>...  Source location

And I cannot find a way to customize the help message like it should be:

Usage: util [OPTIONS] <SOURCE>... <DEST>

Arguments:
  <SOURCE>...  Source location
  <DEST>       Destination location

Can Clap really not handle such a common case? Is there another library which can?

1 Like

While clap does try to be flexible, that doesnt mean that everything will be supported, or straightforward to implement.

From their docs:

  • Flexible enough to port your existing CLI interface

    • However, we won’t necessarily streamline support for each use case

In this case I would look at trailing_var_arg and last

Hi. Thanks for your answer.

I was looking at these options already, but I can't see a way to handle it with these either. I get the impression it really is impossible with clap. And there don't seem to be any other crates which do much better. Really frustrating. I'm coming from Python and there with argparse it isn't a problem at all.

It seems trailing_var_arg can only be used on the last argument, and last makes the -- separator mandatory before the last argument and also prevents it to be used to separate the flag arguments from the second-to-last positionals.

clap::Command::help_template?
You can use the {options} tag in the template to use the auto-generated help text for your options while still changing the usage text.

1 Like

Yes, that seems to do the trick.

It needs to be combined with override_usage(), though, as otherwise the usage output on error will still be wrong. Unfortunately one loses the context-aware usage, but that is not that important for me in this case.