Skip to content

platisd/cpp-command-parser

Repository files navigation

Command Parser

codecov Build, unit tests and coverage CI clang-format CI clang-tidy CI Commit messages

Command Parser is a C++17 header-only utility library for parsing command line "commands".

We define a command as the first CLI argument passed to a binary in the form of:

./your_binary command_name <arg1> <arg2> [arg3]
# `command_name` is the command

A command may optionally take some (boolean) options

./your_binary command_name --option1 --option2 <arg1> <arg2> [arg3]

As long as your CLI application requires the first argument to be a "command", this library will help you parse the command, as well as any subcommands and options.

To put it simply, there must be at least one argument. That first argument to the binary is your "command". If your logic does not require at least one argument to be passed as a "command", then this library is not for you.

Usage

See example_main.cpp for a simple example of how to use the library. In a nutshell:

const auto all = UnparsedCommand::create("all", "Print current configuration");
const auto get = UnparsedCommand::create("get", "Get configuration key", "[-xyz] <key> [default]")
                         .withOptions({ "x", "y", "z" }) // Can be --x or -x etc
                         .withAliases({ "g", "get-key" }); // Alternative IDs for the command instead of "get"
                         .withArgs<std::string, std::optional<std::string>>();
const auto encrypt = UnparsedCommand::create(
                         "encrypt"
                         "Encrypt the given files with the specified policy",
                         "<policy> [file...]")
                         .withArgs<std::string, std::vector<std::string>>();
const std::tuple commands { all, get, encrypt };
const auto parsedCommand = UnparsedCommand::parse(argc, argv, commands);

if (parsedCommand.is(all)) {
    std::cout << "all" << std::endl;
    // auto config = parsedCommand.getArgs(all); // Does not compile because all has no args
} else if (parsedCommand.is(get)) {
    const auto [key, defaultValue] = parsedCommand.getArgs(get);
    const auto x = parsedCommand.hasOption("x");
    const auto y = parsedCommand.hasOption("y");
    const auto z = parsedCommand.hasOption("z");
    std::cout << "get " << key << " " << x << " " << y << " " << z << std::endl;
    if (defaultValue) {
        std::cout << "default " << defaultValue.value() << std::endl;
    }
} else if (parsedCommand.is(encrypt)) {
    const auto [policy, files] = parsedCommand.getArgs(encrypt);
    std::cout << "encrypt " << policy << std::endl;
    for (const auto& file : files) {
        std::cout << "file " << file << std::endl;
    }
} else {
        std::cout << "Available commands:" << std::endl;
        std::cout << parsedCommand.help() << std::endl;
}

Allowed types

The following types are permitted as arguments. They are mandatory unless otherwise specified and their usage rules are enforced during compilation:

  • std::string
  • bool
    • The following (case-insensitive) strings are considered true and everything else false:
      • true
      • yes
      • y
      • on
      • 1
  • int
  • long
  • long long
  • unsigned long
  • unsigned long long
  • float
  • double
  • long double
  • std::optional<T> where T is any of the above types
    • Optional argument: May not be provided but must be at the end of the argument list
  • std::vector<T> where T is any of the above types
    • Zero or more optional arguments: May be of any number or not provided, but cannot precede a mandatory argument and cannot be combined with a std::optional argument

In addition to the above, any user-defined type that is default constructible and constructible from a std::string is also allowed.

Why not insert your favorite CLI parsing library here?

In all fairness, this library was created under the misconception that cxxopts was not able to satisfy our use-case out of the box, which is not true. Some of its cool features are:

  • Compile-time check of argument count and types (i.e. you cannot "forget" to parse an argument or parse one that was not expected)
  • Header-only
  • No dependencies
  • No macros
  • No exceptions

Acknowledgements

This library was developed internally at neat.no and is now open-sourced.

About

Parse CLI commands with compile-time checks for your sanity

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published