#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_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{}; // 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_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); }