refactor: moved impl to cpp and added argument.h

master
cobrapitz 3 years ago
parent 3e7cf374db
commit a45867d6d6

@ -23,6 +23,7 @@ set(CBLIB_HEADERS
${CBLIB_INCLUDE_DIR}/time/time.h ${CBLIB_INCLUDE_DIR}/time/time.h
${CBLIB_INCLUDE_DIR}/test/test.h ${CBLIB_INCLUDE_DIR}/test/test.h
${CBLIB_INCLUDE_DIR}/argparser/argument_parser.h ${CBLIB_INCLUDE_DIR}/argparser/argument_parser.h
${CBLIB_INCLUDE_DIR}/argparser/argument.h
${CBLIB_INCLUDE_DIR}/string_utils/string_utils.h ${CBLIB_INCLUDE_DIR}/string_utils/string_utils.h
) )
set(CBLIB_SOURCES set(CBLIB_SOURCES

@ -0,0 +1,64 @@
#ifndef CBLIB_ARGUMENT_H
#define CBLIB_ARGUMENT_H
#include <string>
#include <cassert>
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

@ -1,6 +1,8 @@
#ifndef REVERSI_ARGUMENTPARSER_H #ifndef CB_ARGUMENTPARSER_H
#define REVERSI_ARGUMENTPARSER_H #define CB_ARGUMENTPARSER_H
#include "argument.h"
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
@ -9,74 +11,22 @@
#include <iostream> #include <iostream>
#include <unordered_map> #include <unordered_map>
#include <cassert> #include <cassert>
#include <algorithm>
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 { namespace cb {
private: using ArgumentMapType = std::unordered_map<std::string, cb::argparser::Argument>;
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();
}
};
using ArgumentMapType = std::unordered_map<std::string, Argument>; namespace cb::argparser {
class ArgumentMap : public ArgumentMapType { class ArgumentMap : public ArgumentMapType {
public: public:
Argument &operator[](std::string &&argumentName) { Argument &operator[](std::string &&argument_name) {
assert(count(argumentName) > 0 && "Argument not found!"); assert(count(argument_name) > 0 && "Argument not found!");
return this->at(argumentName); return this->at(argument_name);
} }
}; };
@ -84,168 +34,43 @@ public:
class ArgumentParser { class ArgumentParser {
private: private:
std::vector<ParseArgument> arguments; std::vector<ParseArgument> arguments;
std::vector<ParseArgument> positionalArguments; std::vector<ParseArgument> positional_arguments;
public: public:
void addArgument(const std::string &shortName, const std::string &name, Type type, std::string description, void add_argument(const std::string &short_name, const std::string &name, Type type, std::string description,
bool defaultValue, Mode mode = Mode::OPTIONAL) { bool default_value,
assert(type == Type::BOOL); Mode mode = Mode::OPTIONAL);
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, void add_argument(const std::string &short_name, const std::string &name, Type type, std::string description,
const char *defaultValue, Mode mode = Mode::OPTIONAL) { const char *default_value,
addArgument(shortName, name, type, std::move(description), std::string{defaultValue}, mode); Mode mode = Mode::OPTIONAL);
}
void addArgument(const std::string &shortName, const std::string &name, Type type, std::string description, void add_argument(const std::string &short_name, const std::string &name, Type type, std::string description,
int defaultValue, Mode mode = Mode::OPTIONAL) { int default_value,
assert(type == Type::INTEGER); Mode mode = Mode::OPTIONAL);
addArgument(shortName, name, type, std::move(description), std::to_string(defaultValue), mode);
}
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);
}
}
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);
}
for (auto &&arg: arguments) {
arg.isPresent = false;
}
ArgumentMap parsedArgs = parseArguments(argc, argv); 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);
verifyRequiredArgumentsPresent(); ArgumentMap parse(int argc, char *argv[]);
return parsedArgs;
}
private: private:
static void insert_argument(ArgumentMap &parsed_args, ParseArgument &positional_arg, const Argument &argument);
static void insertArgument(ArgumentMap &parsedArgs, ParseArgument &positionalArg, const Argument &argument) { ArgumentMap parse_arguments(int argc, char *const *argv);
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;
}
if (requiresAdditionalArgument(arg, argc, argv, i)) {
auto content = std::string{argv[i + 1]};
consumeArgument(parsedArgs, arg, std::move(content));
i++;
} else {
consumeArgument(parsedArgs, arg);
}
}
}
for (auto &&arg: arguments) {
if (parsedArgs.find(arg.name) != parsedArgs.end()) {
continue; // already in parsed args
}
insertArgument(parsedArgs, arg, Argument{arg.defaultValue, arg.type});
}
return parsedArgs;
}
static bool requiresAdditionalArgument(const ParseArgument &parseArgument, int argc, char *const *argv, int i) { static bool requiresAdditionalArgument(const ParseArgument &parseArgument, int argc, char *const *argv, int i);
if (parseArgument.type != Type::BOOL) {
return true;
}
if (i + 1 >= argc) { static void consume_argument(ArgumentMap &parsed_args, ParseArgument &arg, std::string content = "1");
return false;
}
if (argv[i + 1][0] == '-') { void verify_required_arguments_present() const;
return false;
}
return true; static bool matchesArgument(const std::string &text, const ParseArgument &arg);
}
static void consumeArgument(ArgumentMap &parsedArgs, ParseArgument &arg, std::string content = "1") { bool argument_exists(const std::string &short_name, const std::string &name);
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;
});
}
}; };
} }
#endif //REVERSI_ARGUMENTPARSER_H #endif //CB_ARGUMENTPARSER_H

@ -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);
}

Loading…
Cancel
Save