mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-30 04:33:53 +00:00
Merge branch 'master' into basic-enum
This commit is contained in:
commit
3c57be7adf
@ -1,6 +1,7 @@
|
|||||||
## Version 1.1 (in progress)
|
## Version 1.1 (in progress)
|
||||||
* Added support for basic enumerations [#12](https://github.com/CLIUtils/CLI11/issues/12)
|
* Added support for basic enumerations [#12](https://github.com/CLIUtils/CLI11/issues/12)
|
||||||
* Added `app.parse_order()` with original parse order
|
* Added `app.parse_order()` with original parse order
|
||||||
|
* Added `prefix_command()`, which is like `allow_extras` but instantly stops and returns.
|
||||||
|
|
||||||
## Version 1.0
|
## Version 1.0
|
||||||
* Cleanup using `clang-tidy` and `clang-format`
|
* Cleanup using `clang-tidy` and `clang-format`
|
||||||
|
@ -47,7 +47,7 @@ After I wrote this, I also found the following libraries:
|
|||||||
|---------|-------------------|
|
|---------|-------------------|
|
||||||
| [GFlags] | The Google Commandline Flags library. Uses macros heavily, and is limited in scope, missing things like subcommands. It provides a simple syntax and supports config files/env vars. |
|
| [GFlags] | The Google Commandline Flags library. Uses macros heavily, and is limited in scope, missing things like subcommands. It provides a simple syntax and supports config files/env vars. |
|
||||||
| [GetOpt] | Very limited C solution with long, convoluted syntax. Does not support much of anything, like help generation. Always available on UNIX, though (but in different flavors). |
|
| [GetOpt] | Very limited C solution with long, convoluted syntax. Does not support much of anything, like help generation. Always available on UNIX, though (but in different flavors). |
|
||||||
| [ProgramOptions.hxx] | Intresting library, less powerful and no subcommands. |
|
| [ProgramOptions.hxx] | Intresting library, less powerful and no subcommands. Nice callback system. |
|
||||||
| [Args] | Also interesting, and supports subcommands. I like the optional-like design, but CLI11 is cleaner and provides direct value access, and is less verbose. |
|
| [Args] | Also interesting, and supports subcommands. I like the optional-like design, but CLI11 is cleaner and provides direct value access, and is less verbose. |
|
||||||
| [Argument Aggregator] | I'm a big fan of the [fmt] library, and the try-catch statement looks familiar. :thumbsup: Doesn't seem to support subcommands. |
|
| [Argument Aggregator] | I'm a big fan of the [fmt] library, and the try-catch statement looks familiar. :thumbsup: Doesn't seem to support subcommands. |
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ To set up, add options, and run, your main function will look something like thi
|
|||||||
CLI::App app{"App description"};
|
CLI::App app{"App description"};
|
||||||
|
|
||||||
std::string filename = "default";
|
std::string filename = "default";
|
||||||
app.add_option("-f,--file", file, "A help string");
|
app.add_option("-f,--file", filename, "A help string");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
app.parse(argc, argv);
|
app.parse(argc, argv);
|
||||||
@ -194,6 +194,7 @@ There are several options that are supported on the main app and subcommands. Th
|
|||||||
* `.parsed()`: True if this subcommand was given on the command line
|
* `.parsed()`: True if this subcommand was given on the command line
|
||||||
* `.set_callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point.
|
* `.set_callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point.
|
||||||
* `.allow_extras()`: Do not throw an error if extra arguments are left over (Only useful on the main `App`, as that's the one that throws errors).
|
* `.allow_extras()`: Do not throw an error if extra arguments are left over (Only useful on the main `App`, as that's the one that throws errors).
|
||||||
|
* `.prefix_command()`: Like `allow_extras`, but stop immediately on the first unrecognised item. It is ideal for allowing your app to be a "prefix" to calling another app.
|
||||||
|
|
||||||
## Configuration file
|
## Configuration file
|
||||||
|
|
||||||
|
@ -18,3 +18,4 @@ add_cli_exe(simple simple.cpp)
|
|||||||
add_cli_exe(subcommands subcommands.cpp)
|
add_cli_exe(subcommands subcommands.cpp)
|
||||||
add_cli_exe(groups groups.cpp)
|
add_cli_exe(groups groups.cpp)
|
||||||
add_cli_exe(inter_argument_order inter_argument_order.cpp)
|
add_cli_exe(inter_argument_order inter_argument_order.cpp)
|
||||||
|
add_cli_exe(prefix_command prefix_command.cpp)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
CLI::App app;
|
CLI::App app;
|
||||||
@ -25,7 +26,7 @@ int main(int argc, char **argv) {
|
|||||||
std::reverse(std::begin(foos), std::end(foos));
|
std::reverse(std::begin(foos), std::end(foos));
|
||||||
std::reverse(std::begin(bars), std::end(bars));
|
std::reverse(std::begin(bars), std::end(bars));
|
||||||
|
|
||||||
std::vector<std::tuple<std::string, int>> keyval;
|
std::vector<std::pair<std::string, int>> keyval;
|
||||||
for(auto option : app.parse_order()) {
|
for(auto option : app.parse_order()) {
|
||||||
if(option == foo) {
|
if(option == foo) {
|
||||||
keyval.emplace_back("foo", foos.back());
|
keyval.emplace_back("foo", foos.back());
|
||||||
@ -38,11 +39,7 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prove the vector is correct
|
// Prove the vector is correct
|
||||||
std::string name;
|
for(auto &pair : keyval) {
|
||||||
int value;
|
std::cout << pair.first << " : " << pair.second << std::endl;
|
||||||
|
|
||||||
for(auto &tuple : keyval) {
|
|
||||||
std::tie(name, value) = tuple;
|
|
||||||
std::cout << name << " : " << value << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
examples/prefix_command.cpp
Normal file
32
examples/prefix_command.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include "CLI/CLI.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
|
||||||
|
CLI::App app("Prefix command app");
|
||||||
|
app.prefix_command();
|
||||||
|
|
||||||
|
std::vector<int> vals;
|
||||||
|
app.add_option("--vals,-v", vals)
|
||||||
|
->expected(1);
|
||||||
|
|
||||||
|
std::vector<std::string> more_comms;
|
||||||
|
try {
|
||||||
|
more_comms = app.parse(argc, argv);
|
||||||
|
} catch(const CLI::ParseError &e) {
|
||||||
|
return app.exit(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Prefix:";
|
||||||
|
for(int v : vals)
|
||||||
|
std::cout << v << ":";
|
||||||
|
|
||||||
|
std::cout << std::endl << "Remaining commands: ";
|
||||||
|
|
||||||
|
// Perfer to loop over from beginning, not "pop" order
|
||||||
|
std::reverse(std::begin(more_comms), std::end(more_comms));
|
||||||
|
for(auto com : more_comms)
|
||||||
|
std::cout << com << " ";
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -57,6 +57,9 @@ class App {
|
|||||||
/// If true, allow extra arguments (ie, don't throw an error).
|
/// If true, allow extra arguments (ie, don't throw an error).
|
||||||
bool allow_extras_{false};
|
bool allow_extras_{false};
|
||||||
|
|
||||||
|
/// If true, return immediatly on an unrecognised option (implies allow_extras)
|
||||||
|
bool prefix_command_{false};
|
||||||
|
|
||||||
/// This is a function that runs when complete. Great for subcommands. Can throw.
|
/// This is a function that runs when complete. Great for subcommands. Can throw.
|
||||||
std::function<void()> callback_;
|
std::function<void()> callback_;
|
||||||
|
|
||||||
@ -152,6 +155,12 @@ class App {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Do not parse anything after the first unrecongnised option and return
|
||||||
|
App *prefix_command(bool allow = true) {
|
||||||
|
prefix_command_ = allow;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/// Ignore case. Subcommand inherit value.
|
/// Ignore case. Subcommand inherit value.
|
||||||
App *ignore_case(bool value = true) {
|
App *ignore_case(bool value = true) {
|
||||||
ignore_case_ = value;
|
ignore_case_ = value;
|
||||||
@ -949,7 +958,7 @@ class App {
|
|||||||
return val.first != detail::Classifer::POSITIONAL_MARK;
|
return val.first != detail::Classifer::POSITIONAL_MARK;
|
||||||
});
|
});
|
||||||
|
|
||||||
if(num_left_over > 0 && !allow_extras_)
|
if(num_left_over > 0 && !(allow_extras_ || prefix_command_))
|
||||||
throw ExtrasError("[" + detail::rjoin(args, " ") + "]");
|
throw ExtrasError("[" + detail::rjoin(args, " ") + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1060,7 +1069,15 @@ class App {
|
|||||||
else {
|
else {
|
||||||
args.pop_back();
|
args.pop_back();
|
||||||
missing()->emplace_back(detail::Classifer::NONE, positional);
|
missing()->emplace_back(detail::Classifer::NONE, positional);
|
||||||
|
|
||||||
|
if(prefix_command_) {
|
||||||
|
while(!args.empty()) {
|
||||||
|
missing()->emplace_back(detail::Classifer::NONE, args.back());
|
||||||
|
args.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a subcommand, modify args and continue
|
/// Parse a subcommand, modify args and continue
|
||||||
|
@ -433,6 +433,10 @@ class Option {
|
|||||||
/// Set the default value string representation
|
/// Set the default value string representation
|
||||||
void set_default_val(std::string val) { defaultval_ = val; }
|
void set_default_val(std::string val) { defaultval_ = val; }
|
||||||
|
|
||||||
|
|
||||||
|
/// Set the type name displayed on this option
|
||||||
|
void set_type_name(std::string val) {typeval_ = val;}
|
||||||
|
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -190,6 +190,22 @@ TEST(THelp, ExcludesPositional) {
|
|||||||
EXPECT_THAT(help, HasSubstr("Excludes: op1"));
|
EXPECT_THAT(help, HasSubstr("Excludes: op1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(THelp, ManualSetters) {
|
||||||
|
|
||||||
|
CLI::App app{"My prog"};
|
||||||
|
|
||||||
|
int x;
|
||||||
|
|
||||||
|
CLI::Option *op1 = app.add_option("--op", x);
|
||||||
|
op1->set_default_val("12");
|
||||||
|
op1->set_type_name("BIGGLES");
|
||||||
|
|
||||||
|
std::string help = app.help();
|
||||||
|
|
||||||
|
EXPECT_THAT(help, HasSubstr("=12"));
|
||||||
|
EXPECT_THAT(help, HasSubstr("BIGGLES"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(THelp, Subcom) {
|
TEST(THelp, Subcom) {
|
||||||
CLI::App app{"My prog"};
|
CLI::App app{"My prog"};
|
||||||
|
|
||||||
|
@ -231,6 +231,19 @@ TEST_F(TApp, BadSubcomSearch) {
|
|||||||
EXPECT_THROW(app.get_subcommand(two), CLI::OptionNotFound);
|
EXPECT_THROW(app.get_subcommand(two), CLI::OptionNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TApp, PrefixProgram) {
|
||||||
|
|
||||||
|
app.prefix_command();
|
||||||
|
|
||||||
|
app.add_flag("--simple");
|
||||||
|
|
||||||
|
args = {"--simple", "other", "--simple", "--mine"};
|
||||||
|
auto ret_args = run();
|
||||||
|
|
||||||
|
EXPECT_EQ(ret_args, std::vector<std::string>({"--mine", "--simple", "other"}));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
struct SubcommandProgram : public TApp {
|
struct SubcommandProgram : public TApp {
|
||||||
|
|
||||||
CLI::App *start;
|
CLI::App *start;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user