You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
7.6 KiB
C++
212 lines
7.6 KiB
C++
|
|
#include "cb/argparser/argument_parser.h"
|
|
#include "cb/argparser/argument.h"
|
|
|
|
#include <format>
|
|
|
|
|
|
cb::argparser::Argument::Argument(std::string content, cb::argparser::Type type) : content{std::move(content)}, type{type} {
|
|
if (type == Type::BOOL_TYPE && 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);
|
|
}
|
|
|
|
if (mode == Mode::POSITIONAL_MODE) {
|
|
assert(short_name.empty() && "The name should be left empty if it's a positional argument!");
|
|
assert(name.empty() && "The name should be left empty if it's a positional argument!");
|
|
} else {
|
|
assert(!name.empty() && "long name should never be empty!");
|
|
}
|
|
|
|
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_MODE) {
|
|
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{};
|
|
|
|
add_argument("-h", "--help", Type::BOOL_TYPE, "Help message.", false, Mode::OPTIONAL_MODE);
|
|
|
|
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_TYPE) {
|
|
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_MODE) {
|
|
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_TYPE);
|
|
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_TYPE);
|
|
add_argument(short_name, name, type, std::move(description), std::to_string(default_value), mode);
|
|
}
|
|
|
|
std::string cb::argparser::ArgumentParser::get_help() {
|
|
std::string help_message{};
|
|
if (not positional_arguments.empty()) {
|
|
help_message += "Positional Arguments:\n";
|
|
for (auto &&arg: positional_arguments) {
|
|
#ifndef CB_EXCLUDE_FORMATTED_PRINTS
|
|
help_message += std::format("{:15} {}\n",
|
|
arg.name,
|
|
arg.description);
|
|
#endif
|
|
}
|
|
help_message += "\n";
|
|
}
|
|
|
|
if (not arguments.empty()) {
|
|
help_message += "Arguments:\n";
|
|
for (auto &&arg: arguments) {
|
|
#ifndef CB_EXCLUDE_FORMATTED_PRINTS
|
|
help_message += std::format("{:5} {:15} {}\n",
|
|
arg.short_name,
|
|
arg.name,
|
|
arg.description);
|
|
#endif
|
|
}
|
|
help_message += "\n";
|
|
}
|
|
return help_message;
|
|
}
|