refactor: moved impl to cpp and added argument.h
parent
3e7cf374db
commit
a45867d6d6
@ -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 +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…
Reference in New Issue