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.
CBLib/src/cb/argparser/argument_parser.cpp

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