clap-rs / clap

A full featured, fast Command Line Argument Parser for Rust
docs.rs/clap
Apache License 2.0
13.64k stars 1.02k forks source link

Arg::new("set-import").num_args(3).value_names(["asdf"]) are listed 3 times in --help; should only list once #5524

Closed qknight closed 3 weeks ago

qknight commented 3 weeks ago

Please complete the following tasks

Rust Version

1.78.0

Clap Version

4.3.24

Minimal reproducible code

issue

the --help for set-import should look like:

--set-import <file> <from> <to>

but instead it looks like:

--set-import <asdf> <asdf> <asdf>

It will always just repeat what i put into

.num_args(3).value_names(["asdf"])

or

.num_args(3).value_name("file from to")

I also tried subcommand and browsed through all the examples but I couldn't find a mention for a command line option with 3 arguments.

the --help run

(base) PS C:\Users\joschie\Desktop\Projects\fixPath\target\debug> .\fixPath.exe --help
>>> fixPath to modify FS locations of linked DLLs in an PE executable <<<

Usage: fixPath.exe <--version|--list-imports <FILENAME>|--set-import <asdf> <asdf> <asdf>>

Options:
      --version                          Prints the version
      --list-imports <FILENAME>          Calls the list_imports function with a filename
      --set-import <asdf> <asdf> <asdf>  Updates DLL bindings for <from> so it points to <to>
  -h, --help                             Print help

replit

The code is at: https://replit.com/@qknight/test

the code

use clap::{value_parser, Arg, ArgAction, Command};
use std::{env, fs, process};

fn main() {
    let matches = Command::new("fixPath")
        .about(">>> fixPath to modify FS locations of linked DLLs in an PE executable <<<")
        .arg(
            Arg::new("version")
                .long("version")
                .help("Prints the version")
                .action(ArgAction::SetTrue),
        )
        .arg(
            Arg::new("list-imports")
                .long("list-imports")
                .help("Calls the list_imports function with a filename")
                .value_name("FILENAME"),
        )
        .arg(
            Arg::new("set-import")
                .long("set-import")
                .help("Updates DLL bindings for <from> so it points to <to>")
                .num_args(3)
                .value_names(["asdf"]) // FIXME is printed 3 times in --help, why?!
                .required(false),
        )
        .group(
            clap::ArgGroup::new("commands")
                .args(&["version", "list-imports", "set-import"])
                .required(true)
                .multiple(false),
        )
        .get_matches();

    if matches.get_flag("version") {
        println!("fixPath version 1.0"); // FIXME read from cargo.toml
    } else if let Some(filename) = matches.get_one::<String>("list-imports") {
        println!("{}", filename);
    } else if let Some(values) = matches.get_many::<String>("set-import") {
        let args: Vec<&str> = values.map(|s| s.as_str()).collect();
        println!("set-import: {}, {}, {}", args[0], args[1], args[2]);
    }
}

Steps to reproduce the bug with the above code

Use replit or simply copy the contents into a main.rs and use:

[package]
name = "fixPath"
version = "0.1.0"
edition = "2021"

[dependencies]
clap = { version = "4.3.24", features = ["derive"] }

Actual Behaviour

It shows:

--set-import

Expected Behaviour

Should show:

--set-import

Additional Context

No response

Debug Output

No response

epage commented 3 weeks ago

I'm a bit confused by this issue. Its not clear how the following argument:

        .arg(
            Arg::new("set-import")
                .long("set-import")
                .help("Updates DLL bindings for <from> so it points to <to>")
                .num_args(3)
                .value_names(["asdf"]) // FIXME is printed 3 times in --help, why?!
                .required(false),
        )

is supposed to produce the following help output

--set-import <file> <from> <to>

Either (or both)

qknight commented 3 weeks ago

@epage I don't know how I could be more clear.

All I want is that the help page reads:

(base) PS C:\Users\joschie\Desktop\Projects\fixPath\target\debug> .\fixPath.exe --help
>>> fixPath to modify FS locations of linked DLLs in an PE executable <<<

Usage: fixPath.exe <--version|--list-imports <FILENAME>|--set-import <asdf> <asdf> <asdf>>

Options:
      --version                          Prints the version
      --list-imports <file>          Calls the list_imports function with a filename
      --set-import <file> <from> <to>  Updates <file> DLL bindings for <from> so it points to <to>
  -h, --help                             Print help
epage commented 3 weeks ago

Can you provide a reproduction example that attempts that? As I said, the reproduction case in the issue does not. asdf has nothign to do with file, from, or to

qknight commented 3 weeks ago

I think we are done here, thanks anyway.

qknight commented 3 weeks ago

For those who read this issue, my solution is now:

        .arg(
            Arg::new("set-import")
                .long("set-import")
                .short('s')
                .help("Updates DLL <file> bindings for <from> so it points to <to>, ./fixPath test.exe foo.dll c:\\foo.dll")
                .value_name("arg")
                .num_args(3)
                .required(false),
        )

And the help is then:

Usage: fixPath.exe <--version|--list-imports <arg>|--set-import <arg> <arg> <arg>>

Options:
      --version                       Prints the version
  -l, --list-imports <arg>            Lists DLL <file> the list_imports function with a filename
  -s, --set-import <arg> <arg> <arg>  Updates DLL <file> bindings for <from> so it points to <to>, ./fixPath test.exe foo.dll c:\foo.dll
  -h, --help                          Print help