chore: improve argument parser code

This commit is contained in:
Jan 2024-09-23 20:15:52 +02:00
parent da18291c89
commit 2b1c048a4a
No known key found for this signature in database
GPG Key ID: 44B581F78FF5C57C
2 changed files with 44 additions and 42 deletions

View File

@ -1,9 +1,14 @@
#include "ArgumentParser.h" #include "ArgumentParser.h"
#include <sstream> #include "Utils/StringUtils.h"
const std::string PREFIX_LONG = "--"; #include <format>
const std::string PREFIX_SHORT = "-"; #include <iostream>
#include <sstream>
#include <string>
constexpr auto PREFIX_LONG = "--";
constexpr auto PREFIX_SHORT = "-";
ArgumentParser::ArgumentParser(const CommandLineOption* const* options, const size_t optionCount) ArgumentParser::ArgumentParser(const CommandLineOption* const* options, const size_t optionCount)
{ {
@ -18,9 +23,7 @@ bool ArgumentParser::ParseArguments(const int argc, const char** argv)
std::vector<std::string> args(argc); std::vector<std::string> args(argc);
for (int arg = 0; arg < argc; arg++) for (int arg = 0; arg < argc; arg++)
{
args[arg] = argv[arg]; args[arg] = argv[arg];
}
return ParseArguments(args); return ParseArguments(args);
} }
@ -30,26 +33,22 @@ bool ArgumentParser::ParseArguments(std::vector<std::string>& args)
m_matched_arguments.clear(); m_matched_arguments.clear();
m_matched_options.clear(); m_matched_options.clear();
const size_t argCount = args.size(); const auto argCount = args.size();
for (unsigned argIndex = 0; argIndex < argCount; argIndex++) for (unsigned argIndex = 0; argIndex < argCount; argIndex++)
{ {
std::string& arg = args[argIndex]; auto& arg = args[argIndex];
if (arg.compare(0, PREFIX_SHORT.size(), PREFIX_SHORT) == 0) if (arg.compare(0, std::char_traits<char>::length(PREFIX_SHORT), PREFIX_SHORT) == 0)
{ {
// Options should be case insensitive. So before comparing we make the argument lower case. // Options should be case-insensitive. So before comparing we make the argument lower case.
const size_t argStrLen = arg.size(); utils::MakeStringLowerCase(arg);
for (unsigned argStrIndex = 0; argStrIndex < argStrLen; argStrIndex++)
{
arg[argStrIndex] = tolower(arg[argStrIndex]);
}
const CommandLineOption* matchedOption = nullptr; const CommandLineOption* matchedOption = nullptr;
if (arg.compare(0, PREFIX_LONG.size(), PREFIX_LONG) == 0) if (arg.compare(0, std::char_traits<char>::length(PREFIX_LONG), PREFIX_LONG) == 0)
{ {
std::string longName = arg.substr(2); const auto longName = arg.substr(2);
for (auto option : m_command_line_options) for (const auto& option : m_command_line_options)
{ {
if (option->m_long_name == longName) if (option->m_long_name == longName)
{ {
@ -60,9 +59,9 @@ bool ArgumentParser::ParseArguments(std::vector<std::string>& args)
} }
else else
{ {
std::string shortName = arg.substr(1); const auto shortName = arg.substr(1);
for (auto option : m_command_line_options) for (const auto& option : m_command_line_options)
{ {
if (option->m_short_name == shortName) if (option->m_short_name == shortName)
{ {
@ -74,7 +73,7 @@ bool ArgumentParser::ParseArguments(std::vector<std::string>& args)
if (matchedOption == nullptr) if (matchedOption == nullptr)
{ {
printf("Unknown option '%s'.\n", arg.c_str()); std::cout << std::format("Unknown option '{}'.\n", arg);
return false; return false;
} }
@ -82,7 +81,7 @@ bool ArgumentParser::ParseArguments(std::vector<std::string>& args)
{ {
if (!matchedOption->m_multi_use) if (!matchedOption->m_multi_use)
{ {
printf("Option '%s' already specified.\n", arg.c_str()); std::cout << std::format("Option '{}' already specified.\n", arg);
return false; return false;
} }
} }
@ -91,21 +90,21 @@ bool ArgumentParser::ParseArguments(std::vector<std::string>& args)
m_matched_options[matchedOption] = std::vector<std::string>(); m_matched_options[matchedOption] = std::vector<std::string>();
} }
const size_t parameterCount = matchedOption->m_parameters.size(); const auto parameterCount = matchedOption->m_parameters.size();
if (argIndex + parameterCount >= argCount) if (argIndex + parameterCount >= argCount)
{ {
printf("Not enough parameters for option '%s'.\n", arg.c_str()); std::cout << std::format("Not enough parameters for option '{}'.\n", arg);
return false; return false;
} }
std::vector<std::string>& parameters = m_matched_options[matchedOption]; auto& parameters = m_matched_options[matchedOption];
for (unsigned parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++) for (unsigned parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++)
{ {
std::string& param = args[argIndex + parameterIndex + 1]; std::string& param = args[argIndex + parameterIndex + 1];
if (param.compare(0, PREFIX_SHORT.size(), PREFIX_SHORT) == 0) if (param.compare(0, std::char_traits<char>::length(PREFIX_SHORT), PREFIX_SHORT) == 0)
{ {
printf("Not enough parameters for option '%s'.\n", arg.c_str()); std::cout << std::format("Not enough parameters for option '{}'.\n", arg);
return false; return false;
} }
@ -128,18 +127,19 @@ std::vector<std::string> ArgumentParser::GetArguments() const
return m_matched_arguments; return m_matched_arguments;
} }
std::string ArgumentParser::GetValueForOption(const CommandLineOption* option) std::string ArgumentParser::GetValueForOption(const CommandLineOption* option) const
{ {
if (!IsOptionSpecified(option)) const auto existingOption = m_matched_options.find(option);
if (existingOption == m_matched_options.end())
return ""; return "";
std::stringstream value; std::stringstream value;
bool firstMatch = true; bool firstMatch = true;
for (const auto& match : m_matched_options[option]) for (const auto& match : existingOption->second)
{ {
if (!firstMatch) if (!firstMatch)
{ {
value << " " << match; value << ' ' << match;
} }
else else
{ {
@ -151,15 +151,16 @@ std::string ArgumentParser::GetValueForOption(const CommandLineOption* option)
return value.str(); return value.str();
} }
std::vector<std::string> ArgumentParser::GetParametersForOption(const CommandLineOption* option) std::vector<std::string> ArgumentParser::GetParametersForOption(const CommandLineOption* option) const
{ {
if (!IsOptionSpecified(option)) const auto existingOption = m_matched_options.find(option);
if (existingOption == m_matched_options.end())
return std::vector<std::string>(); return std::vector<std::string>();
return m_matched_options[option]; return existingOption->second;
} }
bool ArgumentParser::IsOptionSpecified(const CommandLineOption* option) bool ArgumentParser::IsOptionSpecified(const CommandLineOption* option) const
{ {
return m_matched_options.find(option) != m_matched_options.end(); return m_matched_options.find(option) != m_matched_options.end();
} }

View File

@ -7,19 +7,20 @@
class ArgumentParser class ArgumentParser
{ {
std::vector<const CommandLineOption*> m_command_line_options;
std::map<const CommandLineOption*, std::vector<std::string>> m_matched_options;
std::vector<std::string> m_matched_arguments;
public: public:
ArgumentParser(const CommandLineOption* const* options, size_t optionCount); ArgumentParser(const CommandLineOption* const* options, size_t optionCount);
bool ParseArguments(std::vector<std::string>& args); bool ParseArguments(std::vector<std::string>& args);
bool ParseArguments(int argc, const char** argv); bool ParseArguments(int argc, const char** argv);
std::vector<std::string> GetArguments() const; [[nodiscard]] std::vector<std::string> GetArguments() const;
bool IsOptionSpecified(const CommandLineOption* option); bool IsOptionSpecified(const CommandLineOption* option) const;
std::string GetValueForOption(const CommandLineOption* option); std::string GetValueForOption(const CommandLineOption* option) const;
std::vector<std::string> GetParametersForOption(const CommandLineOption* option); std::vector<std::string> GetParametersForOption(const CommandLineOption* option) const;
private:
std::vector<const CommandLineOption*> m_command_line_options;
std::map<const CommandLineOption*, std::vector<std::string>> m_matched_options;
std::vector<std::string> m_matched_arguments;
}; };