Allow nested operation arguments
eg. syncthingctl pwd rescan
This commit is contained in:
parent
864605f5c8
commit
7c189b18e1
|
@ -120,8 +120,8 @@ set(META_APP_AUTHOR "Martchus")
|
|||
set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
|
||||
set(META_APP_DESCRIPTION "Common C++ classes and routines used by my applications such as argument parser, IO and conversion utilities")
|
||||
set(META_VERSION_MAJOR 4)
|
||||
set(META_VERSION_MINOR 6)
|
||||
set(META_VERSION_PATCH 2)
|
||||
set(META_VERSION_MINOR 7)
|
||||
set(META_VERSION_PATCH 0)
|
||||
|
||||
# find required 3rd party libraries
|
||||
include(3rdParty)
|
||||
|
|
|
@ -195,14 +195,12 @@ void ArgumentReader::read(ArgumentVector &args)
|
|||
}
|
||||
|
||||
// first value might denote "operation"
|
||||
if(!index) {
|
||||
for(Argument *arg : args) {
|
||||
if(arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *argv)) {
|
||||
(matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
|
||||
lastArgDenotation = argv;
|
||||
++index, ++argv;
|
||||
break;
|
||||
}
|
||||
for(Argument *arg : args) {
|
||||
if(arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *argv)) {
|
||||
(matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
|
||||
lastArgDenotation = argv;
|
||||
++index, ++argv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -731,8 +729,8 @@ bool ArgumentParser::isUncombinableMainArgPresent() const
|
|||
* - The same argument has not been added twice to the same parent.
|
||||
* - Only one argument within a parent is default or implicit.
|
||||
* - Only main arguments denote operations.
|
||||
* - Argument abbreviations are unique within each level.
|
||||
* - Argument names are unique within within each level.
|
||||
* - Argument abbreviations are unique within the same level.
|
||||
* - Argument names are unique within within the same level.
|
||||
*
|
||||
* \remarks
|
||||
* - Verifies the sub arguments, too.
|
||||
|
@ -752,7 +750,6 @@ void ApplicationUtilities::ArgumentParser::verifyArgs(const ArgumentVector &args
|
|||
for(const Argument *arg : args) {
|
||||
assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
|
||||
verifiedArgs.push_back(arg);
|
||||
assert(arg->isMainArgument() || !arg->denotesOperation());
|
||||
assert(!arg->isImplicit() || !hasImplicit);
|
||||
hasImplicit |= arg->isImplicit();
|
||||
assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
|
||||
|
@ -1026,7 +1023,7 @@ void ArgumentParser::printBashCompletion(int argc, const char *const *argv, unsi
|
|||
if(reader.argv == reader.end) {
|
||||
cout << '\'' << *(reader.argv - 1) << '\'' << ' ';
|
||||
}
|
||||
} else if(arg->denotesOperation() && (!actualArgumentCount() || (currentWordIndex == 0 && (!lastDetectedArg || (lastDetectedArg->isPresent() && lastDetectedArgIndex == 0))))) {
|
||||
} else if(arg->denotesOperation()) {
|
||||
cout << '\'' << arg->name() << '\'' << ' ';
|
||||
} else {
|
||||
cout << '\'' << '-' << '-' << arg->name() << '\'' << ' ';
|
||||
|
|
|
@ -340,6 +340,25 @@ void ArgumentParserTests::testParsing()
|
|||
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(e.what(), "Not all parameter for argument \"fields\" provided. You have to provide the following parameter: title album artist trackpos"));
|
||||
}
|
||||
|
||||
// nested operations
|
||||
const char *argv14[] = {"tageditor", "get", "fields", "album=test", "-f", "somefile"};
|
||||
parser.resetArgs();
|
||||
fieldsArg.setRequiredValueCount(-1);
|
||||
fieldsArg.setDenotesOperation(true);
|
||||
parser.parseArgs(6, argv14);
|
||||
CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(fieldsArg.isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "album=test"));
|
||||
|
||||
// implicit flag still works when argument doesn't denote operation
|
||||
parser.resetArgs();
|
||||
fieldsArg.setDenotesOperation(false);
|
||||
parser.parseArgs(6, argv14);
|
||||
CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
|
||||
CPPUNIT_ASSERT(fieldsArg.isPresent());
|
||||
CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "fields"));
|
||||
CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(1), "album=test"));
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -453,10 +472,21 @@ void ArgumentParserTests::testBashCompletion()
|
|||
cout.rdbuf(regularCoutBuffer);
|
||||
CPPUNIT_ASSERT_EQUAL("COMPREPLY=('--files' '--values' )\n"s, buffer.str());
|
||||
|
||||
// nested operations should be proposed as operations
|
||||
buffer.str(string());
|
||||
cout.rdbuf(buffer.rdbuf());
|
||||
parser.resetArgs();
|
||||
filesArg.setDenotesOperation(true);
|
||||
reader.reset(argv2, argv2 + 1).read();
|
||||
parser.printBashCompletion(1, argv2, 1, reader);
|
||||
cout.rdbuf(regularCoutBuffer);
|
||||
CPPUNIT_ASSERT_EQUAL("COMPREPLY=('files' '--values' )\n"s, buffer.str());
|
||||
|
||||
// specifying no args should propose all main arguments
|
||||
buffer.str(string());
|
||||
cout.rdbuf(buffer.rdbuf());
|
||||
parser.resetArgs();
|
||||
filesArg.setDenotesOperation(false);
|
||||
reader.reset(nullptr, nullptr).read();
|
||||
parser.printBashCompletion(0, nullptr, 0, reader);
|
||||
cout.rdbuf(regularCoutBuffer);
|
||||
|
|
Loading…
Reference in New Issue