mirror of
https://git.lynn.is/Gwen/argparser.git
synced 2024-05-08 02:31:09 +02:00
365 lines
15 KiB
C++
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> ¶ms) {
|
|
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
|