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