From a45867d6d6b510eb7e735812f49197ad7929f6c1 Mon Sep 17 00:00:00 2001 From: cobrapitz <12397702+cobrapitz@users.noreply.github.com> Date: Sat, 6 May 2023 02:54:36 +0200 Subject: [PATCH] refactor: moved impl to cpp and added argument.h --- CMakeLists.txt | 1 + include/cb/argparser/argument.h | 64 +++++++ include/cb/argparser/argument_parser.h | 243 ++++--------------------- src/cb/argparser/argument_parser.cpp | 173 ++++++++++++++++++ 4 files changed, 272 insertions(+), 209 deletions(-) create mode 100644 include/cb/argparser/argument.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ca4e4b3..5330345 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ set(CBLIB_HEADERS ${CBLIB_INCLUDE_DIR}/time/time.h ${CBLIB_INCLUDE_DIR}/test/test.h ${CBLIB_INCLUDE_DIR}/argparser/argument_parser.h + ${CBLIB_INCLUDE_DIR}/argparser/argument.h ${CBLIB_INCLUDE_DIR}/string_utils/string_utils.h ) set(CBLIB_SOURCES diff --git a/include/cb/argparser/argument.h b/include/cb/argparser/argument.h new file mode 100644 index 0000000..67cb100 --- /dev/null +++ b/include/cb/argparser/argument.h @@ -0,0 +1,64 @@ + +#ifndef CBLIB_ARGUMENT_H +#define CBLIB_ARGUMENT_H + +#include +#include + +namespace cb::argparser { + +enum class Mode { + POSITIONAL, + REQUIRED, + OPTIONAL, +}; + +enum class Type { + STRING, + BOOL, + INTEGER, +}; + +struct ParseArgument { + std::string short_name; + std::string name; + std::string description; + std::string default_value; + Type type; + Mode mode; + + bool is_present{false}; +}; + + +class Argument { +private: + std::string content; + Type type{Type::STRING}; +public: + + Argument() = default; + + explicit Argument(std::string content, Type type = Type::STRING); + + [[nodiscard]] const std::string &as_str() const { + return content; + } + + [[nodiscard]] bool as_bool() const { + return !content.empty(); + } + + int as_int() { + assert(type == Type::INTEGER); + return std::stoi(content); + } + + explicit operator bool() const { + return as_bool(); + } +}; + +} + +#endif //CBLIB_ARGUMENT_H diff --git a/include/cb/argparser/argument_parser.h b/include/cb/argparser/argument_parser.h index 4f0b456..665eae5 100644 --- a/include/cb/argparser/argument_parser.h +++ b/include/cb/argparser/argument_parser.h @@ -1,6 +1,8 @@ -#ifndef REVERSI_ARGUMENTPARSER_H -#define REVERSI_ARGUMENTPARSER_H +#ifndef CB_ARGUMENTPARSER_H +#define CB_ARGUMENTPARSER_H + +#include "argument.h" #include #include @@ -9,74 +11,22 @@ #include #include #include +#include -namespace cb::argparser { - -enum class Mode { - POSITIONAL, - REQUIRED, - OPTIONAL, -}; - -enum class Type { - STRING, - BOOL, - INTEGER, -}; - -struct ParseArgument { - std::string shortName; - std::string name; - std::string description; - std::string defaultValue; - Type type; - Mode mode; - - bool isPresent{false}; -}; - - -class Argument { -private: - std::string content; - Type type{Type::STRING}; -public: - - Argument() = default; - explicit Argument(std::string content, Type type = Type::STRING) : content{std::move(content)}, type{type} { - if (type == Type::BOOL && this->content == "false") { - this->content = ""; - } - } - - [[nodiscard]] const std::string &asStr() const { - return content; - } - - [[nodiscard]] bool asBool() const { - return !content.empty(); - } - - int asInt() { - assert(type == Type::INTEGER); - return std::stoi(content); - } - - explicit operator bool() const { - return asBool(); - } -}; +namespace cb { + using ArgumentMapType = std::unordered_map; +} -using ArgumentMapType = std::unordered_map; +namespace cb::argparser { class ArgumentMap : public ArgumentMapType { public: - Argument &operator[](std::string &&argumentName) { - assert(count(argumentName) > 0 && "Argument not found!"); - return this->at(argumentName); + Argument &operator[](std::string &&argument_name) { + assert(count(argument_name) > 0 && "Argument not found!"); + return this->at(argument_name); } }; @@ -84,168 +34,43 @@ public: class ArgumentParser { private: std::vector arguments; - std::vector positionalArguments; + std::vector positional_arguments; public: - void addArgument(const std::string &shortName, const std::string &name, Type type, std::string description, - bool defaultValue, Mode mode = Mode::OPTIONAL) { - assert(type == Type::BOOL); - std::string value = defaultValue ? "1" : ""; - addArgument(shortName, name, type, std::move(description), value, mode); - } - - void addArgument(const std::string &shortName, const std::string &name, Type type, std::string description, - const char *defaultValue, Mode mode = Mode::OPTIONAL) { - addArgument(shortName, name, type, std::move(description), std::string{defaultValue}, mode); - } - - void addArgument(const std::string &shortName, const std::string &name, Type type, std::string description, - int defaultValue, Mode mode = Mode::OPTIONAL) { - assert(type == Type::INTEGER); - addArgument(shortName, name, type, std::move(description), std::to_string(defaultValue), mode); - } + void add_argument(const std::string &short_name, const std::string &name, Type type, std::string description, + bool default_value, + Mode mode = Mode::OPTIONAL); - void addArgument(const std::string &shortName, const std::string &name, Type type, std::string description, - std::string defaultValue = "", Mode mode = Mode::OPTIONAL) { - if (argumentExists(shortName, name)) { - const auto errorMessage = "ParseArgument already exists --" + name + " -" + shortName; - throw std::runtime_error(errorMessage); - } - - ParseArgument argument{}; - argument.shortName = shortName; - argument.name = name; - argument.type = type; - argument.mode = mode; - argument.description = std::move(description); - argument.defaultValue = std::move(defaultValue); - - if (mode == Mode::POSITIONAL) { - positionalArguments.push_back(argument); - } else { - arguments.push_back(argument); - } - } + void add_argument(const std::string &short_name, const std::string &name, Type type, std::string description, + const char *default_value, + Mode mode = Mode::OPTIONAL); - ArgumentMap parse(int argc, char *argv[]) { - if (argc <= positionalArguments.size()) { - const auto errorMessage = - "Not enough arguments provided"; - // TODO print help message - throw std::runtime_error(errorMessage); - } + void add_argument(const std::string &short_name, const std::string &name, Type type, std::string description, + int default_value, + Mode mode = Mode::OPTIONAL); - for (auto &&arg: arguments) { - arg.isPresent = false; - } + void add_argument(const std::string &short_name, const std::string &name, Type type, std::string description, + std::string default_value = "", + Mode mode = Mode::OPTIONAL); - ArgumentMap parsedArgs = parseArguments(argc, argv); - - verifyRequiredArgumentsPresent(); - - return parsedArgs; - } + ArgumentMap parse(int argc, char *argv[]); private: + static void insert_argument(ArgumentMap &parsed_args, ParseArgument &positional_arg, const Argument &argument); - static void insertArgument(ArgumentMap &parsedArgs, ParseArgument &positionalArg, const Argument &argument) { - parsedArgs.insert_or_assign(positionalArg.name, argument); - if (!positionalArg.shortName.empty()) { - parsedArgs.insert_or_assign(positionalArg.shortName, argument); - } - } - - ArgumentMap parseArguments(int argc, char *const *argv) { - ArgumentMap parsedArgs{}; - - // TODO gen help message - parsedArgs.insert_or_assign("-h", Argument{"Help"}); - parsedArgs.insert_or_assign("--help", Argument{"Help"}); - - for (int i = 0; i < argc; i++) { - bool handled = false; - - for (auto &&positionalArg: positionalArguments) { - insertArgument(parsedArgs, positionalArg, Argument{argv[i], positionalArg.type}); - handled = true; - } - - if (handled) { - continue; - } - - for (auto &&arg: arguments) { - if (!matchesArgument(argv[i], arg)) { - continue; - } + ArgumentMap parse_arguments(int argc, char *const *argv); - if (requiresAdditionalArgument(arg, argc, argv, i)) { - auto content = std::string{argv[i + 1]}; - consumeArgument(parsedArgs, arg, std::move(content)); - i++; - } else { - consumeArgument(parsedArgs, arg); - } - } - } + static bool requiresAdditionalArgument(const ParseArgument &parseArgument, int argc, char *const *argv, int i); + static void consume_argument(ArgumentMap &parsed_args, ParseArgument &arg, std::string content = "1"); - for (auto &&arg: arguments) { - if (parsedArgs.find(arg.name) != parsedArgs.end()) { - continue; // already in parsed args - } + void verify_required_arguments_present() const; - insertArgument(parsedArgs, arg, Argument{arg.defaultValue, arg.type}); - } + static bool matchesArgument(const std::string &text, const ParseArgument &arg); - return parsedArgs; - } - - static bool requiresAdditionalArgument(const ParseArgument &parseArgument, int argc, char *const *argv, int i) { - if (parseArgument.type != Type::BOOL) { - return true; - } - - if (i + 1 >= argc) { - return false; - } - - if (argv[i + 1][0] == '-') { - return false; - } - - return true; - } - - static void consumeArgument(ArgumentMap &parsedArgs, ParseArgument &arg, std::string content = "1") { - insertArgument(parsedArgs, arg, Argument{std::move(content), arg.type}); - arg.isPresent = true; - } - - void verifyRequiredArgumentsPresent() const { - for (auto &&argument: arguments) { - if (argument.mode != Mode::REQUIRED) { - continue; - } - if (!argument.isPresent) { - const auto errorMessage = - "Required argument not present: --" + argument.name + " -" + argument.shortName; - throw std::runtime_error(errorMessage); - } - } - } - - static bool matchesArgument(const std::string &text, const ParseArgument &arg) { - return text == arg.shortName || text == arg.name; - } - - bool argumentExists(const std::string &shortName, const std::string &name) { - return std::any_of(arguments.begin(), arguments.end(), [&](ParseArgument &arg) { - return arg.shortName == shortName || arg.name == name; - }); - } + bool argument_exists(const std::string &short_name, const std::string &name); }; } -#endif //REVERSI_ARGUMENTPARSER_H +#endif //CB_ARGUMENTPARSER_H diff --git a/src/cb/argparser/argument_parser.cpp b/src/cb/argparser/argument_parser.cpp index 8b13789..526c60e 100644 --- a/src/cb/argparser/argument_parser.cpp +++ b/src/cb/argparser/argument_parser.cpp @@ -1 +1,174 @@ +#include "cb/argparser/argument_parser.h" + + +cb::argparser::Argument::Argument(std::string content, cb::argparser::Type type) : content{std::move(content)}, type{type} { + if (type == Type::BOOL && this->content == "false") { + this->content = ""; + } +} + +void cb::argparser::ArgumentParser::add_argument(const std::string &short_name, const std::string &name, + cb::argparser::Type type, std::string description, + std::string default_value, cb::argparser::Mode mode) { + if (argument_exists(short_name, name)) { + const auto error_message = "ParseArgument already exists --" + name + " -" + short_name; + throw std::runtime_error(error_message); + } + + ParseArgument argument{}; + argument.short_name = short_name; + argument.name = name; + argument.type = type; + argument.mode = mode; + argument.description = std::move(description); + argument.default_value = std::move(default_value); + + if (mode == Mode::POSITIONAL) { + positional_arguments.push_back(argument); + } else { + arguments.push_back(argument); + } +} + +cb::argparser::ArgumentMap cb::argparser::ArgumentParser::parse(int argc, char **argv) { + if (argc <= positional_arguments.size()) { + const auto error_message = + "Not enough arguments provided"; + // TODO print help message + throw std::runtime_error(error_message); + } + + for (auto &&arg: arguments) { + arg.is_present = false; + } + + ArgumentMap parsed_args = parse_arguments(argc, argv); + + verify_required_arguments_present(); + + return parsed_args; +} + +void cb::argparser::ArgumentParser::insert_argument(cb::argparser::ArgumentMap &parsed_args, + cb::argparser::ParseArgument &positional_arg, + const cb::argparser::Argument &argument) { + parsed_args.insert_or_assign(positional_arg.name, argument); + if (!positional_arg.short_name.empty()) { + parsed_args.insert_or_assign(positional_arg.short_name, argument); + } +} + +cb::argparser::ArgumentMap cb::argparser::ArgumentParser::parse_arguments(int argc, char *const *argv) { + ArgumentMap parsed_args{}; + + // TODO gen help message + parsed_args.insert_or_assign("-h", Argument{"Help"}); + parsed_args.insert_or_assign("--help", Argument{"Help"}); + + for (int i = 0; i < argc; i++) { + bool handled = false; + + for (auto &&positional_arg: positional_arguments) { + insert_argument(parsed_args, positional_arg, Argument{argv[i], positional_arg.type}); + handled = true; + } + + if (handled) { + continue; + } + + for (auto &&arg: arguments) { + if (!matchesArgument(argv[i], arg)) { + continue; + } + + if (requiresAdditionalArgument(arg, argc, argv, i)) { + auto content = std::string{argv[i + 1]}; + consume_argument(parsed_args, arg, std::move(content)); + i++; + } else { + consume_argument(parsed_args, arg); + } + } + } + + + for (auto &&arg: arguments) { + if (parsed_args.find(arg.name) != parsed_args.end()) { + continue; // already in parsed args + } + + insert_argument(parsed_args, arg, Argument{arg.default_value, arg.type}); + } + + return parsed_args; +} + +bool +cb::argparser::ArgumentParser::requiresAdditionalArgument(const cb::argparser::ParseArgument &parseArgument, int argc, + char *const *argv, int i) { + if (parseArgument.type != Type::BOOL) { + return true; + } + + if (i + 1 >= argc) { + return false; + } + + if (argv[i + 1][0] == '-') { + return false; + } + + return true; +} + +void cb::argparser::ArgumentParser::verify_required_arguments_present() const { + for (auto &&argument: arguments) { + if (argument.mode != Mode::REQUIRED) { + continue; + } + if (!argument.is_present) { + const auto error_message = + "Required argument not present: --" + argument.name + " -" + argument.short_name; + throw std::runtime_error(error_message); + } + } +} + +bool cb::argparser::ArgumentParser::matchesArgument(const std::string &text, const cb::argparser::ParseArgument &arg) { + return text == arg.short_name || text == arg.name; +} + +bool cb::argparser::ArgumentParser::argument_exists(const std::string &short_name, const std::string &name) { + return std::any_of(arguments.begin(), arguments.end(), [&](ParseArgument &arg) { + return arg.short_name == short_name || arg.name == name; + }); +} + +void cb::argparser::ArgumentParser::consume_argument(cb::argparser::ArgumentMap &parsed_args, + cb::argparser::ParseArgument &arg, std::string content) { + insert_argument(parsed_args, arg, Argument{std::move(content), arg.type}); + arg.is_present = true; +} + +void cb::argparser::ArgumentParser::add_argument(const std::string &short_name, const std::string &name, + cb::argparser::Type type, std::string description, bool default_value, + cb::argparser::Mode mode) { + assert(type == Type::BOOL); + std::string value = default_value ? "1" : ""; + add_argument(short_name, name, type, std::move(description), value, mode); +} + +void cb::argparser::ArgumentParser::add_argument(const std::string &short_name, const std::string &name, + cb::argparser::Type type, std::string description, + const char *default_value, cb::argparser::Mode mode) { + add_argument(short_name, name, type, std::move(description), std::string{default_value}, mode); +} + +void cb::argparser::ArgumentParser::add_argument(const std::string &short_name, const std::string &name, + cb::argparser::Type type, std::string description, int default_value, + cb::argparser::Mode mode) { + assert(type == Type::INTEGER); + add_argument(short_name, name, type, std::move(description), std::to_string(default_value), mode); +}