From 645f917e6503ddc5bffeb1b23ed7b6d84ef5d1bb Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Wed, 29 Jun 2016 09:58:46 -0600 Subject: add kick-out tests --- test.cxx | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/test.cxx b/test.cxx index c368bf9..1b6bea7 100644 --- a/test.cxx +++ b/test.cxx @@ -423,7 +423,6 @@ TEST_CASE("An exception should be thrown when a single-argument flag is matched {"yellow", MappingEnum::yellow}, {"green", MappingEnum::green}}; - std::ostream null(nullptr); args::ArgumentParser parser("Test command"); args::Flag foo(parser, "Foo", "Foo", {'f', "foo"}, true); args::ValueFlag bar(parser, "Bar", "Bar", {'b', "bar"}, "", true); @@ -442,3 +441,37 @@ TEST_CASE("An exception should be thrown when a single-argument flag is matched REQUIRE(baz); REQUIRE(args::get(baz) == MappingEnum::green); } + +TEST_CASE("Sub-parsers should work through kick-out", "[args]") +{ + std::unordered_map map{ + {"default", MappingEnum::def}, + {"foo", MappingEnum::foo}, + {"bar", MappingEnum::bar}, + {"red", MappingEnum::red}, + {"yellow", MappingEnum::yellow}, + {"green", MappingEnum::green}}; + + const std::vector args{"--foo", "green", "--bar"}; + + args::ArgumentParser parser1("Test command"); + args::Flag foo1(parser1, "Foo", "Foo", {'f', "foo"}); + args::Flag bar1(parser1, "Bar", "Bar", {'b', "bar"}); + args::MapPositional sub(parser1, "sub", "sub", map); + sub.KickOut(true); + + auto next = parser1.ParseArgs(args); + + args::ArgumentParser parser2("Test command"); + args::Flag foo2(parser2, "Foo", "Foo", {'f', "foo"}); + args::Flag bar2(parser2, "Bar", "Bar", {'b', "bar"}); + + parser1.ParseArgs(next, std::end(args)); + + REQUIRE(foo1); + REQUIRE_FALSE(bar1); + REQUIRE(sub); + REQUIRE(args::get(sub) == MappingEnum::green); + REQUIRE_FALSE(foo2); + REQUIRE(bar2); +} -- cgit v1.2.1 From 0c9c307b0e534c0ee6fa8f58d496446233d6c764 Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Wed, 29 Jun 2016 10:13:50 -0600 Subject: Flag kick-out should work --- args.hxx | 45 ++++++++++++++++++++++++++++++++++++++------- test.cxx | 2 +- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/args.hxx b/args.hxx index 9a5681a..509a5c3 100644 --- a/args.hxx +++ b/args.hxx @@ -380,9 +380,10 @@ namespace args { protected: const std::string name; + bool kickout; public: - NamedBase(const std::string &name, const std::string &help) : Base(help), name(name) {} + NamedBase(const std::string &name, const std::string &help) : Base(help), name(name), kickout(false) {} virtual ~NamedBase() {} virtual std::tuple GetDescription(const std::string &shortPrefix, const std::string &longPrefi, const std::string &shortSeparator, const std::string &longSeparator) const override @@ -396,6 +397,16 @@ namespace args { return name; } + + void KickOut(bool kickout) noexcept + { + this->kickout = kickout; + } + + bool KickOut() const noexcept + { + return kickout; + } }; /** Base class for all flag options @@ -1093,9 +1104,10 @@ namespace args * * \param begin an iterator to the beginning of the argument list * \param end an iterator to the past-the-end element of the argument list + * \return the iterator after the last parsed value. Only useful for kick-out */ template - void ParseArgs(It begin, It end) + It ParseArgs(It begin, It end) { // Reset all Matched statuses to false, for validation. Don't reset values. ResetMatched(); @@ -1160,6 +1172,11 @@ namespace args problem << "Passed an argument into a non-argument flag: " << chunk; throw ParseError(problem.str()); } + + if (base->KickOut()) + { + return ++it; + } } else { std::ostringstream problem; @@ -1174,7 +1191,7 @@ namespace args { const char arg = *argit; - if (Base *base = Match(arg)) + if (FlagBase *base = Match(arg)) { if (ValueFlagBase *argbase = dynamic_cast(base)) { @@ -1213,6 +1230,11 @@ namespace args // Because this argchunk is done regardless break; } + + if (base->KickOut()) + { + return ++it; + } } else { std::ostringstream problem; @@ -1226,6 +1248,11 @@ namespace args if (pos) { pos->ParseValue(chunk); + + if (pos->KickOut()) + { + return ++it; + } } else { std::ostringstream problem; @@ -1240,30 +1267,34 @@ namespace args problem << "Group validation failed somewhere!"; throw ValidationError(problem.str()); } + return end; } /** Parse all arguments. * * \param args an iterable of the arguments + * \return the iterator after the last parsed value. Only useful for kick-out */ template - void ParseArgs(const T &args) + auto ParseArgs(const T &args) -> decltype(std::begin(args)) { - ParseArgs(std::begin(args), std::end(args)); + return ParseArgs(std::begin(args), std::end(args)); } /** Convenience function to parse the CLI from argc and argv * * Just assigns the program name and vectorizes arguments for passing into ParseArgs() + * + * \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. */ - void ParseCLI(const int argc, const char * const * const argv) + bool ParseCLI(const int argc, const char * const * const argv) { if (prog.empty()) { prog.assign(argv[0]); } const std::vector args(argv + 1, argv + argc); - ParseArgs(args); + return ParseArgs(args) == std::end(args); } }; diff --git a/test.cxx b/test.cxx index 1b6bea7..0fc703d 100644 --- a/test.cxx +++ b/test.cxx @@ -466,7 +466,7 @@ TEST_CASE("Sub-parsers should work through kick-out", "[args]") args::Flag foo2(parser2, "Foo", "Foo", {'f', "foo"}); args::Flag bar2(parser2, "Bar", "Bar", {'b', "bar"}); - parser1.ParseArgs(next, std::end(args)); + parser2.ParseArgs(next, std::end(args)); REQUIRE(foo1); REQUIRE_FALSE(bar1); -- cgit v1.2.1 From 25672c086ff803e7d9bd0a0d08a0f6ecd7a919e5 Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Wed, 29 Jun 2016 10:22:53 -0600 Subject: add extra test --- test.cxx | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/test.cxx b/test.cxx index 0fc703d..8113943 100644 --- a/test.cxx +++ b/test.cxx @@ -475,3 +475,50 @@ TEST_CASE("Sub-parsers should work through kick-out", "[args]") REQUIRE_FALSE(foo2); REQUIRE(bar2); } + +TEST_CASE("Kick-out should work via all flags and value flags", "[args]") +{ + const std::vector args{"-a", "-b", "--foo", "-ca", "--bar", "barvalue", "-db"}; + + args::ArgumentParser parser1("Test command"); + args::Flag a1(parser1, "a", "a", {'a'}); + args::Flag b1(parser1, "b", "b", {'b'}); + args::Flag c1(parser1, "c", "c", {'c'}); + args::Flag d1(parser1, "d", "d", {'d'}); + args::Flag foo(parser1, "foo", "foo", {'f', "foo"}); + foo.KickOut(true); + + args::ArgumentParser parser2("Test command"); + args::Flag a2(parser2, "a", "a", {'a'}); + args::Flag b2(parser2, "b", "b", {'b'}); + args::Flag c2(parser2, "c", "c", {'c'}); + args::Flag d2(parser2, "d", "d", {'d'}); + args::ValueFlag bar(parser2, "bar", "bar", {'B', "bar"}); + bar.KickOut(true); + + args::ArgumentParser parser3("Test command"); + args::Flag a3(parser3, "a", "a", {'a'}); + args::Flag b3(parser3, "b", "b", {'b'}); + args::Flag c3(parser3, "c", "c", {'c'}); + args::Flag d3(parser3, "d", "d", {'d'}); + + auto next = parser1.ParseArgs(args); + next = parser2.ParseArgs(next, std::end(args)); + next = parser3.ParseArgs(next, std::end(args)); + REQUIRE(next == std::end(args)); + REQUIRE(a1); + REQUIRE(b1); + REQUIRE_FALSE(c1); + REQUIRE_FALSE(d1); + REQUIRE(foo); + REQUIRE(a2); + REQUIRE_FALSE(b2); + REQUIRE(c2); + REQUIRE_FALSE(d2); + REQUIRE(bar); + REQUIRE(args::get(bar) == "barvalue"); + REQUIRE_FALSE(a3); + REQUIRE(b3); + REQUIRE_FALSE(c3); + REQUIRE(d3); +} -- cgit v1.2.1