From 3c769fa242d507aa9f2b05a2946247cd175ddd64 Mon Sep 17 00:00:00 2001 From: Martchus Date: Sun, 26 Jun 2022 11:20:24 +0200 Subject: [PATCH] Add greedy-flag for argument parser This is useful if one needs to pass subsequent arguments as-is to another nested argument parser. --- application/argumentparser.cpp | 5 +++-- application/argumentparser.h | 1 + tests/argumentparsertests.cpp | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/application/argumentparser.cpp b/application/argumentparser.cpp index aecbfe9..fde521c 100644 --- a/application/argumentparser.cpp +++ b/application/argumentparser.cpp @@ -206,7 +206,8 @@ bool ArgumentReader::read(ArgumentVector &args) // iterate through all argument denotations; loop might exit earlier when an denotation is unknown while (argv != end) { // check whether there are still values to read - if (values && lastArgInLevel->requiredValueCount() != Argument::varValueCount && values->size() < lastArgInLevel->requiredValueCount()) { + if (values && ((lastArgInLevel->requiredValueCount() != Argument::varValueCount) || (lastArgInLevel->flags() & Argument::Flags::Greedy)) + && values->size() < lastArgInLevel->requiredValueCount()) { // read arg as value and continue with next arg values->emplace_back(argDenotation ? argDenotation : *argv); ++index; @@ -1092,7 +1093,7 @@ void ArgumentParser::verifyArgs(const ArgumentVector &args) assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend()); abbreviations.push_back(arg->abbreviation()); assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](const char *name) { return !strcmp(arg->name(), name); }) == names.cend()); - assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0); + assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0 || (arg->flags() & Argument::Flags::Greedy)); names.emplace_back(arg->name()); } for (const Argument *arg : args) { diff --git a/application/argumentparser.h b/application/argumentparser.h index b85576d..c012ef9 100644 --- a/application/argumentparser.h +++ b/application/argumentparser.h @@ -273,6 +273,7 @@ public: Implicit = 0x2, Operation = 0x4, Deprecated = 0x8, + Greedy = 0x10, }; Argument(const char *name, char abbreviation = '\0', const char *description = nullptr, const char *example = nullptr); diff --git a/tests/argumentparsertests.cpp b/tests/argumentparsertests.cpp index 26f0575..e6b9937 100644 --- a/tests/argumentparsertests.cpp +++ b/tests/argumentparsertests.cpp @@ -452,6 +452,27 @@ void ArgumentParserTests::testParsing() CPPUNIT_ASSERT(fieldsArg.isPresent()); CPPUNIT_ASSERT_EQUAL("fields"sv, std::string_view(fieldsArg.values().at(0))); CPPUNIT_ASSERT_EQUAL("album=test"sv, std::string_view(fieldsArg.values().at(1))); + + // greedy-flag + const char *argv19[] = { "tageditor", "get", "--fields", "foo", "bar", "--help" }; + parser.resetArgs(); + try { + parser.parseArgs(6, argv19, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks); + CPPUNIT_FAIL("Exception expected."); + } catch (const ParseError &e) { + CPPUNIT_ASSERT_EQUAL_MESSAGE("--help assumed to be an argument without greedy-flag (leading to error)", + "The argument \"help\" can not be combined with \"get\"."sv, std::string_view(e.what())); + } + parser.resetArgs(); + fieldsArg.setFlags(Argument::Flags::Greedy, true); + parser.parseArgs(6, argv19, ParseArgumentBehavior::CheckConstraints | ParseArgumentBehavior::InvokeCallbacks); + CPPUNIT_ASSERT(displayTagInfoArg.isPresent()); + CPPUNIT_ASSERT(fieldsArg.isPresent()); + CPPUNIT_ASSERT_MESSAGE("--help not considered an argument with greedy-flag", !parser.helpArg().isPresent()); + CPPUNIT_ASSERT_EQUAL("foo"sv, std::string_view(fieldsArg.values().at(0))); + CPPUNIT_ASSERT_EQUAL("bar"sv, std::string_view(fieldsArg.values().at(1))); + CPPUNIT_ASSERT_EQUAL("--help"sv, std::string_view(fieldsArg.values().at(2))); + CPPUNIT_ASSERT_THROW(fieldsArg.values().at(3), std::out_of_range); } /*!