From 1acf1d62cea4d71ed68e621893ec101264fc42e9 Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Thu, 30 Jun 2016 00:12:02 -0600 Subject: make the last of the v5 changes --- .gitignore | 1 + README.md | 293 +++++++++++++++++++++++++++++++----------------------------- args.hxx | 77 ++++++++-------- gitlike.cxx | 128 ++++++++++++++++++++++++++ 4 files changed, 321 insertions(+), 178 deletions(-) create mode 100644 gitlike.cxx diff --git a/.gitignore b/.gitignore index 58a27ec..5056399 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ install_manifest.txt Makefile !/Makefile *-prefix +gitlike diff --git a/README.md b/README.md index 662d8ee..0ac02e4 100644 --- a/README.md +++ b/README.md @@ -52,16 +52,16 @@ It: * Lets you decide not to allow separate-argument argument flags or joined ones (like disallowing `--foo bar`, requiring `--foo=bar`, or the inverse, or the same for short options). +* Allows you to create subparsers somewhat like argparse, through the use of + kick-out flags (check the gitlike.cxx example program for a simple sample of + this) # What does it not do? There are tons of things this library does not do! - ## It will not ever: -* Allow you to create subparsers like argparse in a master parser (you can do - this yourself with iterators and multiple parsers) * Allow one argument flag to take a specific number of arguments (like `--foo first second`). You can instead split that with a flag list (`--foo first --foo second`) or a custom type extraction (`--foo first,second`) @@ -121,144 +121,18 @@ interpreted as a boolean to see if they've been matched. All variables can be pulled (including the boolean match status) with args::get. -# How fast is it? - -This should not really be a question you ask when you are looking for an -argument-parsing library, but every test I've done shows args as being about -65% faster than TCLAP and 220% faster than boost::program_options. - -The simplest benchmark I threw together is the following one, which parses the -command line `-i 7 -c a 2.7 --char b 8.4 -c c 8.8 --char d` with a parser that -parses -i as an int, -c as a list of chars, and the positional parameters as a -list of doubles (the command line was originally much more complex, but TCLAP's -limitations made me trim it down so I could use a common command line across -all libraries. I also have to copy in the arguments list with every run, -because TCLAP permutes its argument list as it runs (and comparison would have -been unfair without comparing all about equally), but that surprisingly didn't -affect much. Also tested is pulling the arguments out, but that was fast -compared to parsing, as would be expected. +# Group validation is weird. How do I get more helpful output for failed validation? -### The run: - -```shell -% g++ -obench bench.cxx -O2 -std=c++11 -lboost_program_options -% ./bench -args seconds to run: 0.895472 -tclap seconds to run: 1.45001 -boost::program_options seconds to run: 1.98972 -% -``` - -### The benchmark: - -```cpp -#undef NDEBUG -#include -#include -#include -#include "args.hxx" -#include -#include -namespace po = boost::program_options; -using namespace std::chrono; -inline bool doubleequals(const double a, const double b) -{ - static const double delta = 0.0001; - const double diff = a - b; - return diff < delta && diff > -delta; -} -int main() -{ - const std::vector carguments({"-i", "7", "-c", "a", "2.7", "--char", "b", "8.4", "-c", "c", "8.8", "--char", "d"}); - const std::vector pcarguments({"progname", "-i", "7", "-c", "a", "2.7", "--char", "b", "8.4", "-c", "c", "8.8", "--char", "d"}); - // args - { - high_resolution_clock::time_point start = high_resolution_clock::now(); - for (unsigned int x = 0; x < 100000; ++x) - { - std::vector arguments(carguments); - args::ArgumentParser parser("This is a test program.", "This goes after the options."); - args::ValueFlag integer(parser, "integer", "The integer flag", {'i', "int"}); - args::ValueFlagList characters(parser, "characters", "The character flag", {'c', "char"}); - args::PositionalList numbers(parser, "numbers", "The numbers position list"); - parser.ParseArgs(arguments); - const int i = args::get(integer); - const std::vector c(args::get(characters)); - const std::vector n(args::get(numbers)); - assert(i == 7); - assert(c[0] == 'a'); - assert(c[1] == 'b'); - assert(c[2] == 'c'); - assert(c[3] == 'd'); - assert(doubleequals(n[0], 2.7)); - assert(doubleequals(n[1], 8.4)); - assert(doubleequals(n[2], 8.8)); - } - high_resolution_clock::duration runtime = high_resolution_clock::now() - start; - std::cout << "args seconds to run: " << duration_cast>(runtime).count() << std::endl; - } - // tclap - { - high_resolution_clock::time_point start = high_resolution_clock::now(); - for (unsigned int x = 0; x < 100000; ++x) - { - std::vector arguments(pcarguments); - TCLAP::CmdLine cmd("Command description message", ' ', "0.9"); - TCLAP::ValueArg integer("i", "int", "The integer flag", false, 0, "integer", cmd); - TCLAP::MultiArg characters("c", "char", "The character flag", false, "characters", cmd); - TCLAP::UnlabeledMultiArg numbers("numbers", "The numbers position list", false, "foo", cmd, false); - cmd.parse(arguments); - const int i = integer.getValue(); - const std::vector c(characters.getValue()); - const std::vector n(numbers.getValue()); - assert(i == 7); - assert(c[0] == 'a'); - assert(c[1] == 'b'); - assert(c[2] == 'c'); - assert(c[3] == 'd'); - assert(doubleequals(n[0], 2.7)); - assert(doubleequals(n[1], 8.4)); - assert(doubleequals(n[2], 8.8)); - } - high_resolution_clock::duration runtime = high_resolution_clock::now() - start; - std::cout << "tclap seconds to run: " << duration_cast>(runtime).count() << std::endl; - } - // boost::program_options - { - high_resolution_clock::time_point start = high_resolution_clock::now(); - for (unsigned int x = 0; x < 100000; ++x) - { - std::vector arguments(carguments); - po::options_description desc("This is a test program."); - desc.add_options() - ("int,i", po::value(), "The integer flag") - ("char,c", po::value>(), "The character flag") - ("numbers", po::value>(), "The numbers flag"); - po::positional_options_description p; - p.add("numbers", -1); - po::variables_map vm; - po::store(po::command_line_parser(carguments).options(desc).positional(p).run(), vm); - const int i = vm["int"].as(); - const std::vector c(vm["char"].as>()); - const std::vector n(vm["numbers"].as>()); - assert(i == 7); - assert(c[0] == 'a'); - assert(c[1] == 'b'); - assert(c[2] == 'c'); - assert(c[3] == 'd'); - assert(doubleequals(n[0], 2.7)); - assert(doubleequals(n[1], 8.4)); - assert(doubleequals(n[2], 8.8)); - } - high_resolution_clock::duration runtime = high_resolution_clock::now() - start; - std::cout << "boost::program_options seconds to run: " << duration_cast>(runtime).count() << std::endl; - } - return 0; -} -``` - -So, on top of being more flexible, smaller, and easier to read, it is faster -than the most common alternatives. +This is unfortunately not possible, given the power of the groups available. +For instance, if you have a group validation that works like +`(A && B) || (C && (D XOR E))`, how is this library going to be able to +determine what exactly when wrong when it fails? It only knows that the +entire expression evaluated false, not specifically what the user did wrong +(and this is doubled over by the fact that validation operations are ordinary +functions without any special meaning to the library). As you are the only one +who understands the logic of your program, if you want useful group messages, +you have to catch the ValidationError as a special case and check your own +groups and spit out messages accordingly. # Is it developed with regression tests? @@ -915,3 +789,142 @@ TEST_CASE("Mapping types work as needed", "[args]") REQUIRE_THROWS_AS(parser.ParseArgs(std::vector{"--mf=YeLLoW"}), args::MapError); } ``` + +# How fast is it? + +This should not really be a question you ask when you are looking for an +argument-parsing library, but every test I've done shows args as being about +65% faster than TCLAP and 220% faster than boost::program_options. + +The simplest benchmark I threw together is the following one, which parses the +command line `-i 7 -c a 2.7 --char b 8.4 -c c 8.8 --char d` with a parser that +parses -i as an int, -c as a list of chars, and the positional parameters as a +list of doubles (the command line was originally much more complex, but TCLAP's +limitations made me trim it down so I could use a common command line across +all libraries. I also have to copy in the arguments list with every run, +because TCLAP permutes its argument list as it runs (and comparison would have +been unfair without comparing all about equally), but that surprisingly didn't +affect much. Also tested is pulling the arguments out, but that was fast +compared to parsing, as would be expected. + +### The run: + +```shell +% g++ -obench bench.cxx -O2 -std=c++11 -lboost_program_options +% ./bench +args seconds to run: 0.895472 +tclap seconds to run: 1.45001 +boost::program_options seconds to run: 1.98972 +% +``` + +### The benchmark: + +```cpp +#undef NDEBUG +#include +#include +#include +#include "args.hxx" +#include +#include +namespace po = boost::program_options; +using namespace std::chrono; +inline bool doubleequals(const double a, const double b) +{ + static const double delta = 0.0001; + const double diff = a - b; + return diff < delta && diff > -delta; +} +int main() +{ + const std::vector carguments({"-i", "7", "-c", "a", "2.7", "--char", "b", "8.4", "-c", "c", "8.8", "--char", "d"}); + const std::vector pcarguments({"progname", "-i", "7", "-c", "a", "2.7", "--char", "b", "8.4", "-c", "c", "8.8", "--char", "d"}); + // args + { + high_resolution_clock::time_point start = high_resolution_clock::now(); + for (unsigned int x = 0; x < 100000; ++x) + { + std::vector arguments(carguments); + args::ArgumentParser parser("This is a test program.", "This goes after the options."); + args::ValueFlag integer(parser, "integer", "The integer flag", {'i', "int"}); + args::ValueFlagList characters(parser, "characters", "The character flag", {'c', "char"}); + args::PositionalList numbers(parser, "numbers", "The numbers position list"); + parser.ParseArgs(arguments); + const int i = args::get(integer); + const std::vector c(args::get(characters)); + const std::vector n(args::get(numbers)); + assert(i == 7); + assert(c[0] == 'a'); + assert(c[1] == 'b'); + assert(c[2] == 'c'); + assert(c[3] == 'd'); + assert(doubleequals(n[0], 2.7)); + assert(doubleequals(n[1], 8.4)); + assert(doubleequals(n[2], 8.8)); + } + high_resolution_clock::duration runtime = high_resolution_clock::now() - start; + std::cout << "args seconds to run: " << duration_cast>(runtime).count() << std::endl; + } + // tclap + { + high_resolution_clock::time_point start = high_resolution_clock::now(); + for (unsigned int x = 0; x < 100000; ++x) + { + std::vector arguments(pcarguments); + TCLAP::CmdLine cmd("Command description message", ' ', "0.9"); + TCLAP::ValueArg integer("i", "int", "The integer flag", false, 0, "integer", cmd); + TCLAP::MultiArg characters("c", "char", "The character flag", false, "characters", cmd); + TCLAP::UnlabeledMultiArg numbers("numbers", "The numbers position list", false, "foo", cmd, false); + cmd.parse(arguments); + const int i = integer.getValue(); + const std::vector c(characters.getValue()); + const std::vector n(numbers.getValue()); + assert(i == 7); + assert(c[0] == 'a'); + assert(c[1] == 'b'); + assert(c[2] == 'c'); + assert(c[3] == 'd'); + assert(doubleequals(n[0], 2.7)); + assert(doubleequals(n[1], 8.4)); + assert(doubleequals(n[2], 8.8)); + } + high_resolution_clock::duration runtime = high_resolution_clock::now() - start; + std::cout << "tclap seconds to run: " << duration_cast>(runtime).count() << std::endl; + } + // boost::program_options + { + high_resolution_clock::time_point start = high_resolution_clock::now(); + for (unsigned int x = 0; x < 100000; ++x) + { + std::vector arguments(carguments); + po::options_description desc("This is a test program."); + desc.add_options() + ("int,i", po::value(), "The integer flag") + ("char,c", po::value>(), "The character flag") + ("numbers", po::value>(), "The numbers flag"); + po::positional_options_description p; + p.add("numbers", -1); + po::variables_map vm; + po::store(po::command_line_parser(carguments).options(desc).positional(p).run(), vm); + const int i = vm["int"].as(); + const std::vector c(vm["char"].as>()); + const std::vector n(vm["numbers"].as>()); + assert(i == 7); + assert(c[0] == 'a'); + assert(c[1] == 'b'); + assert(c[2] == 'c'); + assert(c[3] == 'd'); + assert(doubleequals(n[0], 2.7)); + assert(doubleequals(n[1], 8.4)); + assert(doubleequals(n[2], 8.8)); + } + high_resolution_clock::duration runtime = high_resolution_clock::now() - start; + std::cout << "boost::program_options seconds to run: " << duration_cast>(runtime).count() << std::endl; + } + return 0; +} +``` + +So, on top of being more flexible, smaller, and easier to read, it is faster +than the most common alternatives. diff --git a/args.hxx b/args.hxx index aab92dd..6d5be60 100644 --- a/args.hxx +++ b/args.hxx @@ -85,11 +85,11 @@ namespace args std::vector Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0) { // Preserve existing line breaks - const std::string::size_type newlineloc = in.find('\n'); + const auto newlineloc = in.find('\n'); if (newlineloc != in.npos) { - std::vector first(Wrap(std::string(in, 0, newlineloc), width)); - std::vector second(Wrap(std::string(in, newlineloc + 1), width)); + auto first = Wrap(std::string(in, 0, newlineloc), width); + auto second = Wrap(std::string(in, newlineloc + 1), width); first.insert( std::end(first), std::make_move_iterator(std::begin(second)), @@ -100,7 +100,7 @@ namespace args { firstlinewidth = width; } - std::string::size_type currentwidth = firstlinewidth; + auto currentwidth = firstlinewidth; std::istringstream stream(in); std::vector output; @@ -110,7 +110,7 @@ namespace args { std::string item; stream >> item; - std::string::size_type itemsize = Glyphs(item); + auto itemsize = Glyphs(item); if ((linesize + 1 + itemsize) > currentwidth) { if (linesize > 0) @@ -461,7 +461,7 @@ namespace args virtual std::tuple GetDescription(const std::string &shortPrefix, const std::string &longPrefix, const std::string &shortSeparator, const std::string &longSeparator) const override { std::tuple description; - const std::vector flagStrings(matcher.GetFlagStrings(shortPrefix, longPrefix)); + const auto flagStrings = matcher.GetFlagStrings(shortPrefix, longPrefix); std::ostringstream flagstream; for (auto it = std::begin(flagStrings); it != std::end(flagStrings); ++it) { @@ -489,7 +489,7 @@ namespace args virtual std::tuple GetDescription(const std::string &shortPrefix, const std::string &longPrefix, const std::string &shortSeparator, const std::string &longSeparator) const override { std::tuple description; - const std::vector flagStrings(matcher.GetFlagStrings(shortPrefix, longPrefix, Name(), shortSeparator, longSeparator)); + const auto flagStrings = matcher.GetFlagStrings(shortPrefix, longPrefix, Name(), shortSeparator, longSeparator); std::ostringstream flagstream; for (auto it = std::begin(flagStrings); it != std::end(flagStrings); ++it) { @@ -600,8 +600,8 @@ namespace args { for (Base *child: children) { - PositionalBase *next = dynamic_cast(child); - Group *group = dynamic_cast(child); + auto next = dynamic_cast(child); + auto group = dynamic_cast(child); if (group) { next = group->GetNextPositional(); @@ -626,7 +626,7 @@ namespace args { return true; } - if (Group *group = dynamic_cast(child)) + if (auto group = dynamic_cast(child)) { if (group->HasFlag()) { @@ -687,7 +687,7 @@ namespace args std::vector> descriptions; for (const auto &child: children) { - if (const Group *group = dynamic_cast(child)) + if (const auto group = dynamic_cast(child)) { // Push that group description on the back if not empty unsigned char addindent = 0; @@ -696,14 +696,14 @@ namespace args descriptions.emplace_back(group->help, "", indent); addindent = 1; } - std::vector> groupDescriptions(group->GetChildDescriptions(shortPrefix, longPrefix, shortSeparator, longSeparator, indent + addindent)); + auto groupDescriptions = group->GetChildDescriptions(shortPrefix, longPrefix, shortSeparator, longSeparator, indent + addindent); descriptions.insert( std::end(descriptions), std::make_move_iterator(std::begin(groupDescriptions)), std::make_move_iterator(std::end(groupDescriptions))); - } else if (const NamedBase *named = dynamic_cast(child)) + } else if (const auto named = dynamic_cast(child)) { - const std::tuple description(named->GetDescription(shortPrefix, longPrefix, shortSeparator, longSeparator)); + const auto description = named->GetDescription(shortPrefix, longPrefix, shortSeparator, longSeparator); descriptions.emplace_back(std::get<0>(description), std::get<1>(description), indent); } } @@ -719,7 +719,7 @@ namespace args { if (const Group *group = dynamic_cast(child)) { - std::vector groupNames(group->GetPosNames()); + auto groupNames = group->GetPosNames(); names.insert( std::end(names), std::make_move_iterator(std::begin(groupNames)), @@ -773,7 +773,7 @@ namespace args { for (const auto child: group.Children()) { - if (const Group *group = dynamic_cast(child)) + if (const auto group = dynamic_cast(child)) { if (!group->Matched()) { @@ -999,8 +999,8 @@ namespace args bool hasoptions = false; bool hasarguments = false; - const std::vector description(Wrap(this->description, helpParams.width - helpParams.descriptionindent)); - const std::vector epilog(Wrap(this->epilog, helpParams.width - helpParams.descriptionindent)); + const auto description = Wrap(this->description, helpParams.width - helpParams.descriptionindent); + const auto epilog = Wrap(this->epilog, helpParams.width - helpParams.descriptionindent); std::ostringstream prognameline; prognameline << prog; if (HasFlag()) @@ -1023,7 +1023,7 @@ namespace args { prognameline << ' ' << proglinePostfix; } - const std::vector proglines(Wrap(prognameline.str(), helpParams.width - (helpParams.progindent + 4), helpParams.width - helpParams.progindent)); + const auto proglines = Wrap(prognameline.str(), helpParams.width - (helpParams.progindent + 4), helpParams.width - helpParams.progindent); auto progit = std::begin(proglines); if (progit != std::end(proglines)) { @@ -1037,7 +1037,7 @@ namespace args help << '\n'; - for (const std::string &line: description) + for (const auto &line: description) { help << std::string(helpParams.descriptionindent, ' ') << line << "\n"; } @@ -1045,9 +1045,9 @@ namespace args help << std::string(helpParams.progindent, ' ') << "OPTIONS:\n\n"; for (const auto &description: GetChildDescriptions(shortprefix, longprefix, allowJoinedShortValue ? "" : " ", allowJoinedLongValue ? longseparator : " ")) { - const unsigned int groupindent = std::get<2>(description) * helpParams.eachgroupindent; - const std::vector flags(Wrap(std::get<0>(description), helpParams.width - (helpParams.flagindent + helpParams.helpindent + helpParams.gutter))); - const std::vector info(Wrap(std::get<1>(description), helpParams.width - (helpParams.helpindent + groupindent))); + const auto groupindent = std::get<2>(description) * helpParams.eachgroupindent; + const auto flags = Wrap(std::get<0>(description), helpParams.width - (helpParams.flagindent + helpParams.helpindent + helpParams.gutter)); + const auto info = Wrap(std::get<1>(description), helpParams.width - (helpParams.helpindent + groupindent)); std::string::size_type flagssize = 0; for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit) @@ -1078,14 +1078,14 @@ namespace args } if (hasoptions && hasarguments && helpParams.showTerminator) { - for (const std::string &item: Wrap(std::string("\"") + terminator + "\" can be used to terminate flag options and force all following arguments to be treated as positional options", helpParams.width - helpParams.flagindent)) + for (const auto &item: Wrap(std::string("\"") + terminator + "\" can be used to terminate flag options and force all following arguments to be treated as positional options", helpParams.width - helpParams.flagindent)) { help << std::string(helpParams.flagindent, ' ') << item << '\n'; } } help << "\n"; - for (const std::string &line: epilog) + for (const auto &line: epilog) { help << std::string(helpParams.descriptionindent, ' ') << line << "\n"; } @@ -1118,7 +1118,7 @@ namespace args // Check all arg chunks for (auto it = begin; it != end; ++it) { - const std::string &chunk = *it; + const auto &chunk = *it; if (!terminated and chunk == terminator) { @@ -1126,16 +1126,17 @@ namespace args // If a long arg was found } else if (!terminated && chunk.find(longprefix) == 0 && chunk.size() > longprefix.size()) { - const std::string argchunk(chunk.substr(longprefix.size())); + const auto argchunk = chunk.substr(longprefix.size()); // Try to separate it, in case of a separator: const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator); - const std::string arg = (separator != argchunk.npos ? + // If the separator is in the argument, separate it. + const auto arg = (separator != argchunk.npos ? std::string(argchunk, 0, separator) : argchunk); - if (FlagBase *base = Match(arg)) + if (auto base = Match(arg)) { - if (ValueFlagBase *argbase = dynamic_cast(base)) + if (auto argbase = dynamic_cast(base)) { if (separator != argchunk.npos) { @@ -1188,14 +1189,14 @@ namespace args // Check short args } else if (!terminated && chunk.find(shortprefix) == 0 && chunk.size() > shortprefix.size()) { - const std::string argchunk(chunk.substr(shortprefix.size())); + const auto argchunk = chunk.substr(shortprefix.size()); for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit) { - const char arg = *argit; + const auto arg = *argit; - if (FlagBase *base = Match(arg)) + if (auto base = Match(arg)) { - if (ValueFlagBase *argbase = dynamic_cast(base)) + if (auto argbase = dynamic_cast(base)) { const std::string value(++argit, std::end(argchunk)); if (!value.empty()) @@ -1246,7 +1247,7 @@ namespace args } } else { - PositionalBase *pos = GetNextPositional(); + auto pos = GetNextPositional(); if (pos) { pos->ParseValue(chunk); @@ -1289,7 +1290,7 @@ namespace args * * \return whether or not all arguments were parsed. This works for detecting kick-out, but is generally useless as it can't do anything with it. */ - bool ParseCLI(const int argc, const char * const * const argv) + bool ParseCLI(const int argc, const char * const * argv) { if (prog.empty()) { @@ -1377,7 +1378,7 @@ namespace args virtual FlagBase *Match(const std::string &arg) override { - FlagBase *me = FlagBase::Match(arg); + auto me = FlagBase::Match(arg); if (me) { ++count; @@ -1387,7 +1388,7 @@ namespace args virtual FlagBase *Match(const char arg) override { - FlagBase *me = FlagBase::Match(arg); + auto me = FlagBase::Match(arg); if (me) { ++count; diff --git a/gitlike.cxx b/gitlike.cxx new file mode 100644 index 0000000..bea4395 --- /dev/null +++ b/gitlike.cxx @@ -0,0 +1,128 @@ +/* Copyright © 2016 Taylor C. Richberger + * This code is released under the license described in the LICENSE file + */ + +#include +#include +#include +#include + +void Init(const std::string &progname, std::vector::const_iterator beginargs, std::vector::const_iterator endargs); +void Add(const std::string &progname, std::vector::const_iterator beginargs, std::vector::const_iterator endargs); + +using commandtype = std::function::const_iterator, std::vector::const_iterator)>; + +int main(int argc, char **argv) +{ + std::unordered_map map{ + {"init", Init}, + {"add", Add}}; + + const std::vector args(argv + 1, argv + argc); + args::ArgumentParser parser("This is a git-like program", "Valid commands are init and add"); + args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); + parser.Prog(argv[0]); + args::Flag version(parser, "version", "Show the version of this program", {"version"}); + args::ValueFlag htmlpath(parser, "html-path", "Specify the html path", {"html-path"}); + args::MapPositional command(parser, "command", "Command to execute", map); + command.KickOut(true); + try + { + auto next = parser.ParseArgs(args); + std::cout << std::boolalpha; + std::cout << "Before command options:" << std::endl; + std::cout << "Version called: " << bool{version} << std::endl; + std::cout << "html-path called: " << bool{htmlpath} << ", value: " << args::get(htmlpath) << std::endl; + if (command) + { + args::get(command)(argv[0], next, std::end(args)); + } else + { + std::cout << parser; + } + } + catch (args::Help) + { + std::cout << parser; + return 0; + } + catch (args::Error e) + { + std::cerr << e.what() << std::endl; + std::cerr << parser; + return 1; + } + return 0; +} + +void Init(const std::string &progname, std::vector::const_iterator beginargs, std::vector::const_iterator endargs) +{ + std::cout << "In Init" << std::endl; + args::ArgumentParser parser(""); + parser.Prog(progname + " init"); + args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); + args::ValueFlag templatedir(parser, "template-directory", "directory from which templates will be used", {"template"}); + args::Flag bare(parser, "bare", "create a bare repository", {"bare"}); + args::Flag quiet(parser, "quiet", "be quiet", {'q', "quiet"}); + args::Positional directory(parser, "directory", "The directory to create in", "."); + try + { + parser.ParseArgs(beginargs, endargs); + std::cout << std::boolalpha; + std::cout << "templatedir: " << bool{templatedir} << ", value: " << args::get(templatedir) << std::endl; + std::cout << "bare: " << bool{bare} << std::endl; + std::cout << "quiet: " << bool{quiet} << std::endl; + std::cout << "directory: " << bool{directory} << ", value: " << args::get(directory) << std::endl; + } + catch (args::Help) + { + std::cout << parser; + return; + } + catch (args::ParseError e) + { + std::cerr << e.what() << std::endl; + std::cerr << parser; + return; + } +} + +void Add(const std::string &progname, std::vector::const_iterator beginargs, std::vector::const_iterator endargs) +{ + std::cout << "In Add" << std::endl; + args::ArgumentParser parser(""); + parser.Prog(progname + " add"); + args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); + args::Flag dryrun(parser, "dryrun", "dry run", {'n', "dry-run"}); + args::Flag verbose(parser, "verbose", "be verbose", {'v', "verbose"}); + args::Flag refresh(parser, "refresh", "Don't add, only refresh the index", {"refresh"}); + args::PositionalList pathspec(parser, "pathspec", "pathspecs"); + try + { + parser.ParseArgs(beginargs, endargs); + std::cout << std::boolalpha; + std::cout << "dryrun: " << bool{dryrun} << std::endl;; + std::cout << "verbose: " << bool{verbose} << std::endl; + std::cout << "refresh: " << bool{refresh} << std::endl; + std::cout << "pathspec: " << bool{pathspec} << std::endl; + if (pathspec) + { + std::cout << "values: " << std::endl; + for (const auto &spec: args::get(pathspec)) + { + std::cout << " - " << spec << std::endl; + } + } + } + catch (args::Help) + { + std::cout << parser; + return; + } + catch (args::ParseError e) + { + std::cerr << e.what() << std::endl; + std::cerr << parser; + return; + } +} -- cgit v1.2.1