Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Positional before command? #125

Open
fakuivan opened this issue Sep 10, 2024 · 1 comment
Open

Positional before command? #125

fakuivan opened this issue Sep 10, 2024 · 1 comment

Comments

@fakuivan
Copy link

I'm trying to add a required positional before a group of commands but the parsing isn't working correctly. Here's an example:

#include "args/args.hxx"

int main(int argc, const char **argv) {
  args::ArgumentParser parser("Compress and decompress leveldb DB");
  args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"});
  args::CompletionFlag completion(parser, {"completion"});
  args::Positional<std::string> db_file(parser, "input", "Input database file",
                                        args::Options::Required);
  args::Group commands(parser, "commands");
  args::Command compress(commands, "compress",
                         "Enables compression on a database");
  args::Command decompress(commands, "decompress",
                           "Remove compression on a database");
  try {
    parser.ParseCLI(argc, argv);
  } catch (const args::Completion &e) {
    std::cout << e.what();
    return 0;
  } catch (const args::Help &) {
    std::cout << parser;
    return 0;
  } catch (const args::ParseError &e) {
    std::cerr << e.what() << std::endl;
    std::cerr << parser;
    return 1;
  }
  std::cout << "Input database is: " << args::get(db_file) << std::endl;
  if (compress) {
    std::cout << "Compressing database" << std::endl;
  }
  if (decompress) {
    std::cout << "Decompressing database" << std::endl;
  }
  return 0;
}

It should work like this:

$ ./main input-file compress
Unknown command: input-file
  ./main COMMAND {OPTIONS} input

    Compress and decompress leveldb DB

  OPTIONS:

      -h, --help                        Display this help menu
      input                             Input database file
      commands
        compress                          Enables compression on a database
        decompress                        Remove compression on a database
      "--" can be used to terminate flag options and force all following
      arguments to be treated as positional options

Not like this, but the command is recognized

$ ./main compress input-file
Passed in argument, but no positional arguments were ready to receive it: input-file
  ./main compress

    Enables compression on a database

  OPTIONS:

And finally only with the command, the input field is still being requested.

$ ./main compress
terminate called after throwing an instance of 'args::RequiredError'
  what():  Option 'input' is required
Aborted (core dumped)
@Taywee
Copy link
Owner

Taywee commented Sep 11, 2024

Unfortunately, commands are always sorted before all positionals at this point. They're really designed with the assumption that they will be the only expected positional for that parser, though the command underneath them can contain its own positionals.

I think I consider this a bug, but the way that commands are designed makes it a lot of effort to fix this bug, and I probably won't be able to get to it for quite some time. I'd recommend either re-ordering them to put any positionals after commands (ideally in a command callable with a subparser), using a MapFlag to choose your operation instead of a command, or taking your input argument via a flag. It's not a perfect answer, unfortunately. Personally, I'd probably use a MapFlag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants