lib/cli: Fix the usage text generator not dealing well with absent sections

BodgeMaster-unfinished
BodgeMaster 2022-08-12 11:50:33 +02:00
parent cb7b5ddba7
commit a1f16e6f6b
2 changed files with 122 additions and 20 deletions

View File

@ -260,7 +260,11 @@ namespace CLI {
} }
std::string ArgumentsParser::getUsage(){ std::string ArgumentsParser::getUsage(){
std::string usageString = "Help: " + this->programName + "\n\n\t" + this->description + "\n\n" + "Usage: " + this->programName; std::string usageString = "";
if (this->description != "") {
usageString += "Help: " + this->programName + "\n\n\t" + this->description + "\n\n";
}
usageString += "Usage: " + this->programName + " ";
if(!this->flagsByShortName.empty()){ if(!this->flagsByShortName.empty()){
usageString += "[-"; usageString += "[-";
@ -284,14 +288,19 @@ namespace CLI {
usageString += argument.placeholder + " "; usageString += argument.placeholder + " ";
} }
usageString += "\n\nFlags:\n"; usageString.push_back('\n');
if(!this->flagsByShortName.empty()){
usageString += "\nFlags:\n";
for(const auto& [key, value]: this->flagsByShortName){ for(const auto& [key, value]: this->flagsByShortName){
usageString += "\t-"; usageString += "\t-";
usageString.push_back(key); usageString.push_back(key);
usageString += ", --" + value->longName + "\n\t\t" + value->description + "\n"; usageString += ", --" + value->longName + "\n\t\t" + value->description + "\n";
} }
}
if(!this->optionsByShortName.empty()){
usageString += "\nOptions:\n"; usageString += "\nOptions:\n";
for(const auto& [key, value]: this->optionsByShortName){ for(const auto& [key, value]: this->optionsByShortName){
@ -299,12 +308,15 @@ namespace CLI {
usageString.push_back(key); usageString.push_back(key);
usageString += " " + value->placeholder + ", --" + value->longName + "=" + value->placeholder + "\n\t\t" + value->description + "\n"; usageString += " " + value->placeholder + ", --" + value->longName + "=" + value->placeholder + "\n\t\t" + value->description + "\n";
} }
}
if(!this->arguments.empty()){
usageString += "\nArguments:\n"; usageString += "\nArguments:\n";
for(const auto& argument: this->arguments){ for(const auto& argument: this->arguments){
usageString += "\t" + argument.placeholder + "\n\t\t" + argument.description + "\n"; usageString += "\t" + argument.placeholder + "\n\t\t" + argument.description + "\n";
} }
}
return usageString; return usageString;
} }

View File

@ -23,7 +23,7 @@
int main() { int main() {
std::cout << "################################################################################" << std::endl; std::cout << "################################################################################" << std::endl;
std::cout << "CLI argument parser tests" << std::endl; std::cout << "CLI argument parsing tests" << std::endl;
std::cout << "################################################################################" << std::endl; std::cout << "################################################################################" << std::endl;
// Valid parameter test ############################################ // Valid parameter test ############################################
@ -636,6 +636,11 @@ int main() {
delete[] tooManyArgumentsTestParameterList; delete[] tooManyArgumentsTestParameterList;
std::cout << "Passed too many arguments test." << std::endl; std::cout << "Passed too many arguments test." << std::endl;
std::cout << "################################################################################" << std::endl;
std::cout << "CLI argument parser help tests" << std::endl;
std::cout << "################################################################################" << std::endl;
// normal usage with all types of CLI input ########################
std::vector<CLI::Flag> helpTestFlags; std::vector<CLI::Flag> helpTestFlags;
helpTestFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head.")); helpTestFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head."));
helpTestFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head.")); helpTestFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head."));
@ -653,8 +658,93 @@ int main() {
CLI::ArgumentsParser helpTestParser = CLI::ArgumentsParser(1, helpTestCommand, helpTestFlags, helpTestOptions, helpTestArguments, "Create a universe with a banana and an apple."); CLI::ArgumentsParser helpTestParser = CLI::ArgumentsParser(1, helpTestCommand, helpTestFlags, helpTestOptions, helpTestArguments, "Create a universe with a banana and an apple.");
ASSERT(helpTestParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-ay] [-b BANANA] [-c CORNHUBBBBB] WASH SHAKE \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n"); ASSERT(helpTestParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-ay] [-b BANANA] [-c CORNHUBBBBB] WASH SHAKE \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n");
std::cout << "Passed Argument Parser usage test." << std::endl; std::cout << "Passed normal usage test." << std::endl;
// no description ##################################################
std::vector<CLI::Flag> usageOnlyTestFlags;
usageOnlyTestFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head."));
usageOnlyTestFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head."));
std::vector<CLI::Option> usageOnlyTestOptions;
usageOnlyTestOptions.push_back(CLI::Option('b', "banana", "BANANA", "smack someone with a ripe banana."));
usageOnlyTestOptions.push_back(CLI::Option('c', "corn", "CORNHUBBBBB", "visit cornhub."));
std::vector<CLI::Argument> usageOnlyTestArguments;
usageOnlyTestArguments.push_back(CLI::Argument("WASH", "Number of times to wash my shark."));
usageOnlyTestArguments.push_back(CLI::Argument("SHAKE", "Number of times to shake fist at cloud."));
const char** usageOnlyTestCommand = new const char*[1];
usageOnlyTestCommand[0] = "universecreator";
CLI::ArgumentsParser usageOnlyTestParser = CLI::ArgumentsParser(1, usageOnlyTestCommand, usageOnlyTestFlags, usageOnlyTestOptions, usageOnlyTestArguments);
ASSERT(usageOnlyTestParser.getUsage() == "Usage: universecreator [-ay] [-b BANANA] [-c CORNHUBBBBB] WASH SHAKE \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n");
std::cout << "Passed normal usage without description test." << std::endl;
// no flags ########################################################
std::vector<CLI::Flag> noFlagsFlags;
std::vector<CLI::Option> noFlagsOptions;
noFlagsOptions.push_back(CLI::Option('b', "banana", "BANANA", "smack someone with a ripe banana."));
noFlagsOptions.push_back(CLI::Option('c', "corn", "CORNHUBBBBB", "visit cornhub."));
std::vector<CLI::Argument> noFlagsArguments;
noFlagsArguments.push_back(CLI::Argument("WASH", "Number of times to wash my shark."));
noFlagsArguments.push_back(CLI::Argument("SHAKE", "Number of times to shake fist at cloud."));
const char** noFlagsCommand = new const char*[1];
noFlagsCommand[0] = "universecreator";
CLI::ArgumentsParser noFlagsParser = CLI::ArgumentsParser(1, noFlagsCommand, noFlagsFlags, noFlagsOptions, noFlagsArguments, "Create a universe with a banana and an apple.");
ASSERT(noFlagsParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-b BANANA] [-c CORNHUBBBBB] WASH SHAKE \n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n");
// no options ######################################################
std::vector<CLI::Flag> noOptionsFlags;
noOptionsFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head."));
noOptionsFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head."));
std::vector<CLI::Option> noOptionsOptions;
std::vector<CLI::Argument> noOptionsArguments;
noOptionsArguments.push_back(CLI::Argument("WASH", "Number of times to wash my shark."));
noOptionsArguments.push_back(CLI::Argument("SHAKE", "Number of times to shake fist at cloud."));
const char** noOptionsCommand = new const char*[1];
noOptionsCommand[0] = "universecreator";
CLI::ArgumentsParser noOptionsParser = CLI::ArgumentsParser(1, noOptionsCommand, noOptionsFlags, noOptionsOptions, noOptionsArguments, "Create a universe with a banana and an apple.");
ASSERT(noOptionsParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-ay] WASH SHAKE \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n");
// no arguments ####################################################
std::vector<CLI::Flag> noArgumentsFlags;
noArgumentsFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head."));
noArgumentsFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head."));
std::vector<CLI::Option> noArgumentsOptions;
noArgumentsOptions.push_back(CLI::Option('b', "banana", "BANANA", "smack someone with a ripe banana."));
noArgumentsOptions.push_back(CLI::Option('c', "corn", "CORNHUBBBBB", "visit cornhub."));
std::vector<CLI::Argument> noArgumentsArguments;
const char** noArgumentsCommand = new const char*[1];
noArgumentsCommand[0] = "universecreator";
CLI::ArgumentsParser noArgumentsParser = CLI::ArgumentsParser(1, noArgumentsCommand, noArgumentsFlags, noArgumentsOptions, noArgumentsArguments, "Create a universe with a banana and an apple.");
ASSERT(noArgumentsParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-ay] [-b BANANA] [-c CORNHUBBBBB] \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n");
std::cout << "Passed absent section usage test." << std::endl;
// no CLI input ####################################################
std::vector<CLI::Flag> noCLIFlags;
std::vector<CLI::Option> noCLIOptions;
std::vector<CLI::Argument> noCLIArguments;
const char** noCLICommand = new const char*[1];
noCLICommand[0] = "universecreator";
CLI::ArgumentsParser noCLIParser = CLI::ArgumentsParser(1, noCLICommand, noCLIFlags, noCLIOptions, noCLIArguments, "Create a universe with a banana and an apple.");
ASSERT(noCLIParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator \n");
std::cout << "Passed no CLI input usage test." << std::endl;
return 0; return 0;
} }