argparser/src/parser.h
2023-05-17 10:17:56 +02:00

365 lines
15 KiB
C++

#ifndef ARGPARSER_PARSER_H
#define ARGPARSER_PARSER_H
#include "argument.h"
#include "basic-type.h"
#include "builtin_parser.h"
#include "defs.h"
#include "distinct-types.h"
#include "errors.h"
#include "list-type.h"
#include "option.h"
#include "optional-arg.h"
#include "parse-result.h"
#include "repeat-arg.h"
#include "repeat-flag.h"
#include "repeat-opt.h"
#include "single-arg.h"
#include "single-flag.h"
#include "single-opt.h"
#include "tuple-type.h"
#include "type.h"
#include "union-type.h"
#include <any>
#include <deque>
#include <functional>
#include <iomanip>
#include <map>
#include <memory>
#include <span>
#include <string>
#include <tuple>
#include <vector>
// TODO: help output
namespace argparser {
class parser {
void validate_opt_or_flag_name(const std::string &name) {
if (!name.starts_with("--")) {
if (!name.starts_with("-") || name.length() != 2) {
throw errors::invalid_option_name_error(name);
}
}
if (options.find(name) != options.end()) {
throw errors::duplicate_option_name(name);
}
}
bool allow_remaining_args_{};
public:
internal::string_map<option_handle> options;
std::vector<arg_handle> arguments;
template<typename T>
[[nodiscard]] option_handle_impl<T> option(const std::string &name, const type_handle_impl<T> &type) {
validate_opt_or_flag_name(name);
auto o = std::make_shared<option_impl<T>>(name, type);
options[name] = o;
return o;
}
template<typename T>
[[nodiscard]] option_handle_impl<T> option(const std::string &name, const std::string &alt_name, const type_handle_impl<T> &type) {
validate_opt_or_flag_name(name);
validate_opt_or_flag_name(alt_name);
auto o = std::make_shared<option_impl<T>>(name, type);
options[name] = o;
options[alt_name] = o;
return o;
}
template<typename T>
[[nodiscard]] repeatable_option_handle_impl<T> repeatable_option(const std::string &name, const type_handle_impl<T> &type) {
validate_opt_or_flag_name(name);
auto o = std::make_shared<repeatable_option_impl<T>>(name, type);
options[name] = o;
return o;
}
template<typename T>
[[nodiscard]] repeatable_option_handle_impl<T> repeatable_option(const std::string &name, const std::string &alt_name, const type_handle_impl<T> &type) {
validate_opt_or_flag_name(name);
validate_opt_or_flag_name(alt_name);
auto o = std::make_shared<repeatable_option_impl<T>>(name, type);
options[name] = o;
options[alt_name] = o;
return o;
}
[[nodiscard]] flag_handle_impl flag(const std::string &name) {
validate_opt_or_flag_name(name);
auto f = std::make_shared<flag_impl>(name);
options[name] = f;
return f;
}
[[nodiscard]] flag_handle_impl flag(const std::string &name, const std::string &alt_name) {
validate_opt_or_flag_name(name);
validate_opt_or_flag_name(alt_name);
auto f = std::make_shared<flag_impl>(name);
options[name] = f;
options[alt_name] = f;
return f;
}
[[nodiscard]] repeatable_flag_handle_impl repeatable_flag(const std::string &name) {
validate_opt_or_flag_name(name);
auto f = std::make_shared<repeatable_flag_impl>(name);
options[name] = f;
return f;
}
[[nodiscard]] repeatable_flag_handle_impl repeatable_flag(const std::string &name, const std::string &alt_name) {
validate_opt_or_flag_name(name);
validate_opt_or_flag_name(alt_name);
auto f = std::make_shared<repeatable_flag_impl>(name);
options[name] = f;
options[alt_name] = f;
return f;
}
template<typename T>
[[nodiscard]] single_arg_handle<T> arg(const std::string &name, type_handle_impl<T> type) {
auto a = std::make_shared<single_arg<T>>(name, type);
arguments.push_back(a);
return a;
}
template<typename T>
[[nodiscard]] optional_arg_handle<T> optional_arg(const std::string name, type_handle_impl<T> type) {
auto a = std::make_shared<argparser::optional_arg<T>>(name, type);
arguments.push_back(a);
return a;
}
template<typename T>
[[nodiscard]] repeatable_arg_handle<T> repeatable_arg(const std::string name, type_handle_impl<T> type) {
auto a = std::make_shared<argparser::repeatable_arg<T>>(name, type);
arguments.push_back(a);
return a;
}
[[nodiscard]] type_handle_impl<std::string> enum_type(const std::string &name, const std::vector<std::string> &values) {
internal::string_map<std::string> actual_values{};
for (const auto &item: values) {
actual_values[item] = item;
}
auto parser_fn = internal::make_enum_parser(name, actual_values);
auto t = std::make_shared<argparser::basic_type<std::string>>(name, parser_fn);
known_types[name] = t;
return t;
}
template<typename T>
[[nodiscard]] type_handle_impl<T> enum_type(const std::string &name, const internal::string_map<T> &values) {
auto parser_fn = internal::make_enum_parser(name, values);
auto t = std::make_shared<argparser::basic_type<T>>(name, parser_fn);
known_types[name] = t;
return t;
}
template<typename... Ts>
[[nodiscard]] type_handle_impl<std::tuple<Ts...>>
tuple_type(const std::string &name, const type_handle_impl<Ts> &...types) {
auto t = std::make_shared<argparser::tuple_type<Ts...>>(name, std::forward_as_tuple(types...));
known_types[name] = t;
return t;
}
template<typename R, typename... Ts>
[[nodiscard]] type_handle_impl<R> tuple_type(const std::string &name, const type_handle_impl<Ts> &...types) {
auto t = std::make_shared<custom_tuple_type<R, Ts...>>(name, std::forward_as_tuple(types...));
known_types[name] = t;
return t;
}
template<typename R, typename... Ts>
[[nodiscard]] type_handle_impl<R> tuple_type(const std::string &name, typename std::type_identity<std::function<R(Ts...)>>::type constructor, const type_handle_impl<Ts> &...types) {
auto t = std::make_shared<custom_tuple_type_with_constructor<R, Ts...>>(name, std::forward_as_tuple(types...), constructor);
known_types[name] = t;
return t;
}
template<typename R, typename... Ts>
[[nodiscard]] type_handle_impl<R> tuple_type(const std::string &name, const std::tuple<type_handle_impl<Ts>...> &types, typename std::type_identity<std::function<R(Ts...)>>::type constructor) {
auto t = std::make_shared<custom_tuple_type_with_constructor<R, Ts...>>(name, std::move(types), constructor);
known_types[name] = t;
return t;
}
template<typename... Ts>
[[nodiscard]] internal::if_not_single_type<type_handle_impl<internal::distinct_types_variant<Ts...>>, Ts...>
union_type(const std::string &name, const type_handle_impl<Ts> &...types) {
auto t = std::make_shared<argparser::union_type<Ts...>>(name, std::forward_as_tuple(types...));
known_types[name] = t;
return t;
}
template<typename... Ts>
[[nodiscard]] type_handle_impl<internal::single_type<Ts...>>
union_type(const std::string &name, const type_handle_impl<Ts> &...types) {
auto t = std::make_shared<argparser::union_type<Ts...>>(name, std::forward_as_tuple(types...));
known_types[name] = t;
return t;
}
template<typename T>
[[nodiscard]] type_handle_impl<std::vector<T>> list_type(const std::string &name, const type_handle_impl<T> &type) {
auto t = std::make_shared<argparser::list_type<T>>(name, type);
known_types[name] = t;
return t;
}
internal::string_map<type_handle> known_types;
template<typename T, internal::parse_opt parse_opt = internal::parse_opt::None>
[[nodiscard]] type_handle_impl<T> basic_type(const std::string &name) {
auto parse_fn = internal::automatic_parser<T, parse_opt>::make_parser(name);
auto t = std::make_shared<argparser::basic_type<T>>(name, parse_fn);
known_types[name] = t;
return t;
}
void enable_remaining_args() {
allow_remaining_args_ = true;
}
[[nodiscard]] parse_result parse(const std::vector<std::string> &params) {
std::deque<std::string> params_queue(params.size());
std::copy(params.begin(), params.end(), params_queue.begin());
auto pr = parse_result{};
auto arg_iter = arguments.begin();
while (!params_queue.empty()) {
auto current = params_queue.front();
bool looks_like_option = false;
option_handle opt{};
std::string opt_name;
if (allow_remaining_args_ && current == "--") {
params_queue.pop_front();
pr.set_remaining(std::vector<std::string>(std::make_move_iterator(params_queue.begin()), std::make_move_iterator(params_queue.end())));
params_queue.clear();
break;
}
if (current.starts_with("--")) {
looks_like_option = true;
if (current.contains('=')) {
opt_name = current.substr(0, current.find_first_of('='));
auto optit = options.find(opt_name);
if (optit != options.end()) {
opt = optit->second;
if (!opt->consumes_value()) {
throw errors::unexpected_option_value_error(opt_name);
}
auto remain = current.substr(opt_name.length() + 1);
params_queue.pop_front();
params_queue.push_front(remain);
}
} else {
opt_name = current;
auto optit = options.find(opt_name);
if (optit != options.end()) {
opt = optit->second;
params_queue.pop_front();
}
}
} else if (current.starts_with("-") && current.length() >= 2) {
looks_like_option = true;
if (current.length() == 2) {
opt_name = current;
auto optit = options.find(opt_name);
if (optit != options.end()) {
opt = optit->second;
params_queue.pop_front();
}
} else {
opt_name = current.substr(0, 2);
auto optit = options.find(opt_name);
if (optit != options.end()) {
opt = optit->second;
auto remain = current.substr(2);
params_queue.pop_front();
if (opt->consumes_value()) {
params_queue.push_front(remain);
} else {
params_queue.push_front("-" + remain);
}
}
}
}
if (opt == nullptr) {
if (arg_iter == arguments.end()) {
if (allow_remaining_args_) {
pr.set_remaining(std::vector<std::string>(std::make_move_iterator(params_queue.begin()), std::make_move_iterator(params_queue.end())));
params_queue.clear();
} else {
if (looks_like_option) {
throw errors::unknown_option_error(opt_name);
} else {
throw errors::too_many_arguments_error();
}
}
} else {
auto arg = arg_iter->get();
try {
arg->parse(current, pr);
params_queue.pop_front();
} catch (errors::runtime_error &) {
if (!arg->has_parsed_enough(pr)) {
if (looks_like_option) {
throw errors::unknown_option_error(opt_name);
} else {
throw;
}
}
if (std::next(arg_iter) == arguments.end()) {
throw;
}
arg_iter = std::next(arg_iter);
continue;// retry parsing the value with the next arg
}
if (!arg->can_parse_more(pr)) {
arg_iter = std::next(arg_iter);
}
}
} else {
std::optional<std::string> option_val = std::nullopt;
auto consumes_value = opt->consumes_value();
if (consumes_value) {
if (params_queue.empty()) {
throw errors::missing_option_value_error(opt_name);
}
option_val = params_queue.front();
params_queue.pop_front();
}
std::any val = {};
auto canonical_name = opt->get_name();
if (pr.has_opt(canonical_name)) {
val = pr.get_opt(canonical_name);
}
try {
opt->parse(option_val, val);
} catch (errors::type_parsing_error &) {
if (option_val.has_value() && options.find(option_val.value()) != options.end()) {
throw errors::missing_option_value_error(opt_name);
} else {
throw;
}
}
pr.set_opt(canonical_name, val);
}
}
if (arg_iter != arguments.end() && !arg_iter->get()->has_parsed_enough(pr)) {
throw errors::missing_argument_error(arg_iter->get()->get_name());
}
for (const auto &[name, option]: options) {
option->validate(pr);
}
return pr;
}
};
}// namespace argparser
#endif//ARGPARSER_PARSER_H