argparser/dist/include/argparser/argparser.h
2023-05-17 10:17:56 +02:00

1585 lines
65 KiB
C++

#include <any>
#include <cassert>
#include <charconv>
#include <deque>
#include <format>
#include <functional>
#include <iomanip>
#include <map>
#include <memory>
#include <optional>
#include <span>
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
namespace argparser {
namespace errors {
class runtime_error : public std::runtime_error {
public:
template<typename... Ts>
explicit runtime_error(Ts... args) : std::runtime_error(args...) {}
};
class logic_error : public std::logic_error {
public:
template<typename... Ts>
explicit logic_error(Ts... args) : std::logic_error(args...) {}
};
class missing_option_error : public runtime_error {
public:
explicit missing_option_error(std::string option_name) : runtime_error(std::format("missing required option {}", option_name)) {}
};
class missing_argument_error : public runtime_error {
public:
explicit missing_argument_error(std::string arg_name) : runtime_error(std::format("missing argument {}", arg_name)) {}
};
class unknown_option_error : public runtime_error {
public:
explicit unknown_option_error(std::string option_name) : runtime_error(std::format("unknown option {}", option_name)) {}
};
class wrong_option_count_error : public runtime_error {
public:
wrong_option_count_error(const std::string &option_name, std::optional<unsigned int> min, std::optional<unsigned int> max, unsigned int actual)
: runtime_error(make_message(option_name, min, max, actual)) {}
private:
static std::string make_message(std::string option_name, std::optional<unsigned int> min, std::optional<unsigned int> max, unsigned int actual) {
if (min != std::nullopt && max != std::nullopt) {
return std::format("option {} was provided {} times, but is required at least {} times and at most {} times", option_name, actual, min.value(), max.value());
} else if (min != std::nullopt) {
return std::format("option {} was provided {} times, but is required at least {} times", option_name, actual, min.value());
} else {
return std::format("option {} was provided {} times, but is required at most {} times", option_name, actual, max.value());
}
}
};
class missing_option_value_error : public runtime_error {
public:
explicit missing_option_value_error(std::string option_name) : runtime_error(std::format("missing value for option {}", option_name)) {}
};
class not_enough_arguments_error : public runtime_error {
public:
explicit not_enough_arguments_error(size_t min_required) : runtime_error(std::format("not enough arguments, need at least {}", min_required)) {}
};
class too_many_arguments_error : public runtime_error {
public:
explicit too_many_arguments_error() : runtime_error("too many arguments") {}
};
class invalid_option_value_error : public runtime_error {
public:
explicit invalid_option_value_error(std::string option_name) : runtime_error(std::format("invalid value for option {}", option_name)) {}
};
class unexpected_option_value_error : public runtime_error {
public:
explicit unexpected_option_value_error(std::string option_name) : runtime_error(std::format("unexpected value for option {}", option_name)) {}
};
class type_parsing_error : public runtime_error {
public:
type_parsing_error(std::string type_name, std::string input, size_t error_pos, std::string message)
: runtime_error(std::format("error parsing type {} at position {}: {}", type_name, error_pos, message)),
type_name(type_name), input(input), error_pos(error_pos) {}
const std::string type_name;
const std::string input;
const int error_pos;
};
class ambiguous_parse_error : public type_parsing_error {
public:
explicit ambiguous_parse_error(std::string type_name, std::string input, size_t error_pos, std::vector<std::string> possible_types)
: type_parsing_error(type_name, input, error_pos, make_message(possible_types)) {}
private:
static std::string make_message(std::vector<std::string> possible_types) {
std::string message = "ambiguity between ";
for (auto it = possible_types.begin(); it != possible_types.end(); it++) {
if (std::next(it) == possible_types.end()) {
message += " and " + *it;
} else {
message += ", " + *it;
}
}
return message;
}
};
class invalid_option_name_error : public logic_error {
public:
explicit invalid_option_name_error(std::string option_name) : logic_error(std::format("invalid option name {}", option_name)) {}
};
class duplicate_option_name : public logic_error {
public:
explicit duplicate_option_name(std::string option_name) : logic_error(std::format("option with name {} already exists", option_name)) {}
};
class duplicate_argument_name : public logic_error {
public:
explicit duplicate_argument_name(std::string arg_name) : logic_error(std::format("argument with name {} already exists", arg_name)) {}
};
class empty_enum_map_error : public logic_error {
public:
explicit empty_enum_map_error(std::string type_name) : logic_error(std::format("no enum values for type {}", type_name)) {}
};
}// namespace errors
namespace internal {
constexpr std::string_view whitespace = " \f\n\r\t\v";
enum class parser_allow_undelimited {
None = 0,
Comma = 1,
Parenthesis = 2,
Brackets = 4,
Any = 7,
};
constexpr parser_allow_undelimited operator|(parser_allow_undelimited a, parser_allow_undelimited b) {
return static_cast<parser_allow_undelimited>(static_cast<std::underlying_type_t<parser_allow_undelimited>>(a) | static_cast<std::underlying_type_t<parser_allow_undelimited>>(b));
}
constexpr parser_allow_undelimited operator&(parser_allow_undelimited a, parser_allow_undelimited b) {
return static_cast<parser_allow_undelimited>(static_cast<std::underlying_type_t<parser_allow_undelimited>>(a) & static_cast<std::underlying_type_t<parser_allow_undelimited>>(b));
}
enum class parse_opt {
None = 0,
BareString = 1,
SingleQuotedString = 2,
DoubleQuotedString = 4,
AnyString = 7,
TrimString = 8,
TrimBareString = 16,
};
constexpr parse_opt operator|(parse_opt a, parse_opt b) {
return static_cast<parse_opt>(static_cast<std::underlying_type_t<parse_opt>>(a) | static_cast<std::underlying_type_t<parse_opt>>(b));
}
constexpr parse_opt operator&(parse_opt a, parse_opt b) {
return static_cast<parse_opt>(static_cast<std::underlying_type_t<parse_opt>>(a) & static_cast<std::underlying_type_t<parse_opt>>(b));
}
template<typename T>
constexpr bool enum_flag_contains(T a, T b) {
return static_cast<std::underlying_type_t<T>>(a & b) != 0;
}
template<typename T>
using string_map = std::map<std::string, T, std::less<>>;
}// namespace internal
class parse_result {
internal::string_map<std::any> opts{};
internal::string_map<std::any> args{};
std::vector<std::string> remaining_args{};
public:
void set_opt(const std::string &name, std::any value) {
opts[name] = std::move(value);
}
[[nodiscard]] std::any get_opt(const std::string &name) const {
if (opts.find(name) != opts.end()) {
return opts.at(name);
} else {
return {};
}
}
[[nodiscard]] bool has_opt(const std::string &name) const {
return opts.find(name) != opts.end();
}
void set_arg(const std::string &name, std::any value) {
args[name] = std::move(value);
}
[[nodiscard]] std::any get_arg(const std::string &name) const {
if (args.find(name) != args.end()) {
return args.at(name);
} else {
return {};
}
}
[[nodiscard]] bool has_arg(const std::string &name) const {
auto argit = args.find(name);
if (argit == args.end())
return false;
return argit != args.end() && argit->second.has_value();
}
void set_remaining(std::vector<std::string> remaining) {
remaining_args = std::move(remaining);
}
[[nodiscard]] std::vector<std::string> remaining() const {
return remaining_args;
}
};
class type {
public:
virtual ~type() = default;
[[nodiscard]] std::string get_name() const {
return name;
}
protected:
explicit type(std::string name) : name(std::move(name)) {}
std::string name;
};
using type_handle = std::shared_ptr<type>;
template<typename T>
class type_impl : public type {
public:
virtual T parse(const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited allow_undelimited = internal::parser_allow_undelimited::None) = 0;
protected:
explicit type_impl(std::string name) : type(std::move(name)) {}
};
template<typename T>
using type_handle_impl = std::shared_ptr<type_impl<T>>;
class arg {
public:
explicit arg(std::string name) : name(std::move(name)) {}
virtual ~arg() = default;
void parse(const std::string &input, parse_result &pr) const {
do_parse(input, pr);
}
[[nodiscard]] bool has_parsed_enough(parse_result &pr) const {
return this->get_has_parsed_enough(pr);
}
[[nodiscard]] bool can_parse_more(parse_result &pr) const {
return this->get_can_parse_more(pr);
}
[[nodiscard]] std::string get_name() const {
return name;
}
protected:
const std::string name;
template<typename T>
static T parse_single_value(std::string input, const type_handle_impl<T> &type) {
const char *parse_end;
auto begin = &*input.begin();
auto end = &*input.end();
auto val = type->parse(begin, end, parse_end, internal::parser_allow_undelimited::Any);
if (parse_end != end) {
throw errors::type_parsing_error(type->get_name(), std::string(begin, end), parse_end - begin, "unexpected input");
}
return val;
}
private:
virtual void do_parse(std::string input, parse_result &pr) const = 0;
[[nodiscard]] virtual bool get_has_parsed_enough(parse_result &pr) const = 0;
[[nodiscard]] virtual bool get_can_parse_more(parse_result &pr) const = 0;
};
using arg_handle = std::shared_ptr<arg>;
namespace internal {
template<typename T>
using parser_func = std::function<T(const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited allow_undelimited)>;
}
template<typename T>
class basic_type : public type_impl<T> {
public:
basic_type(std::string name, internal::parser_func<T> parser) : type_impl<T>(std::move(name)), parser(std::move(parser)) {}
T parse(const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited allow_undelimited) override {
const char *n_parse_end;
auto val = parser(begin, end, n_parse_end, allow_undelimited);
parse_end = n_parse_end;
return val;
}
private:
internal::parser_func<T> parser;
};
namespace internal {
template<typename T, parse_opt parse_opt>
class automatic_parser {
public:
static parser_func<T> make_parser(const std::string &name) {
return [name](const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited) {
T val;
auto pos = begin;
while (pos < end && isspace(*pos)) pos++;
auto res = std::from_chars(pos, end, val);
if (res.ec == std::errc{}) {
parse_end = res.ptr;
return val;
} else {
throw errors::type_parsing_error(name, std::string(begin, end), pos - begin, "invalid number");
}
};
}
};
constexpr parse_opt string_parse_opt_with_default(parse_opt opt) {
if ((opt & parse_opt::AnyString) == parse_opt::None) {
return opt | parse_opt::AnyString;
} else {
return opt;
}
}
constexpr bool string_parser_enable_bare(parse_opt opt) {
return enum_flag_contains(string_parse_opt_with_default(opt), parse_opt::BareString);
}
constexpr bool string_parser_enable_single_quoted(parse_opt opt) {
return enum_flag_contains(string_parse_opt_with_default(opt), parse_opt::SingleQuotedString);
}
constexpr bool string_parser_enable_double_quoted(parse_opt opt) {
return enum_flag_contains(string_parse_opt_with_default(opt), parse_opt::DoubleQuotedString);
}
template<parse_opt parse_opt>
class automatic_parser<std::string, parse_opt> {
static std::string trim_string(const std::string &str) {
auto start = str.find_first_not_of(whitespace);
auto end = str.find_last_not_of(whitespace);
return str.substr(start == std::string::npos ? 0 : start, end == std::string::npos ? 0 : end);
}
public:
static parser_func<std::string> make_parser(const std::string &name) {
return [name](const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited allow_undelimited) {
auto str = std::string_view(begin, end);
if (str.length() == 0) {
if (parse_opt == parse_opt::None || enum_flag_contains(parse_opt, parse_opt::BareString)) {
parse_end = begin;
return std::string("");
} else {
throw errors::type_parsing_error(name, std::string(""), 0, "unexpected empty input");
}
}
if (str[0] == '\'' && string_parser_enable_single_quoted(parse_opt)) {
auto ss = std::stringstream(std::string(str));
std::string val;
ss >> std::quoted(val, '\'', '\\');
auto len = ss.tellg();
parse_end = begin + len;
if (enum_flag_contains(parse_opt, parse_opt::TrimString)) {
return trim_string(val);
} else {
return val;
}
}
if (str[0] == '"' && string_parser_enable_double_quoted(parse_opt)) {
auto ss = std::stringstream(std::string(str));
std::string val;
ss >> std::quoted(val, '"', '\\');
auto len = ss.tellg();
parse_end = begin + len;
if (enum_flag_contains(parse_opt, parse_opt::TrimString)) {
return trim_string(val);
} else {
return val;
}
}
if (string_parser_enable_bare(parse_opt)) {
std::string illegal_characters = "\"'";
if (!internal::enum_flag_contains(allow_undelimited, internal::parser_allow_undelimited::Comma)) {
illegal_characters += ",";
}
if (!internal::enum_flag_contains(allow_undelimited, internal::parser_allow_undelimited::Parenthesis)) {
illegal_characters += "()";
}
if (!internal::enum_flag_contains(allow_undelimited, internal::parser_allow_undelimited::Brackets)) {
illegal_characters += "[]";
}
auto pos = str.find_first_of(illegal_characters);
if (pos == std::string_view::npos) {
parse_end = end;
} else {
parse_end = begin + pos;
}
std::string val{begin, parse_end};
if (enum_flag_contains(parse_opt, parse_opt::TrimString) || enum_flag_contains(parse_opt, parse_opt::TrimBareString)) {
return trim_string(val);
} else {
return val;
}
}
throw errors::type_parsing_error(name, std::string(begin, end), 0, "failed to parse string");
};
}
};
template<typename T>
parser_func<T> make_enum_parser(const std::string &name, internal::string_map<T> values) {
if (values.size() == 0) {
throw errors::empty_enum_map_error(name);
}
return [name, values](const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited) {
auto input = std::basic_string_view(begin, end);
auto start = input.find_first_not_of(whitespace);
if (start == std::string::npos) {
input = "";
} else {
input = std::basic_string_view(begin + start, end);
}
T value;
size_t value_len = 0;
for (const auto &[str, val]: values) {
if (str.length() <= input.length()) {
if (input.starts_with(str)) {
if (str.length() > value_len) {
value = val;
value_len = str.length();
}
}
}
}
if (value_len == 0) {
std::string expected_values = values.begin()->first;
for (auto it = std::next(values.begin()); it != values.end(); it++) {
if (std::next(it) != values.end()) {
expected_values += ", " + it->first;
} else {
expected_values += " or " + it->first;
}
}
throw errors::type_parsing_error(name, std::string(begin, end), 0, std::format("expected {}", expected_values));
} else {
parse_end = begin + value_len;
return value;
}
};
}
}// namespace internal
namespace internal::distinct_types_impl {
template<typename TList, typename... Ts>
struct UniqueTypes;
template<template<typename...> typename X, typename... Ts>
struct UniqueTypes<X<Ts...>> {
using type = X<Ts...>;
};
template<template<typename...> typename X, typename... Ts, typename T, typename... Us>
struct UniqueTypes<X<Ts...>, T, Us...> {
using type = typename UniqueTypes<
typename std::conditional<
std::disjunction<std::is_same<T, Ts>...>::value,
X<Ts...>,
X<Ts..., T>>::type,
Us...>::type;
};
template<template<typename...> typename X, typename... Ts>
struct Distinct {
using type = typename UniqueTypes<X<>, Ts...>::type;
};
}// namespace internal::distinct_types_impl
namespace internal {
template<typename... Ts>
using distinct_types_variant = typename internal::distinct_types_impl::Distinct<std::variant, Ts...>::type;
template<typename... Ts>
using distinct_types_tuple = typename internal::distinct_types_impl::Distinct<std::tuple, Ts...>::type;
template<typename T, typename... Ts>
using if_single_type = std::enable_if_t<std::tuple_size<distinct_types_tuple<Ts...>>::value == 1, T>;
template<typename T, typename... Ts>
using if_not_single_type = std::enable_if_t<(std::tuple_size<distinct_types_tuple<Ts...>>::value > 1), T>;
template<typename T, typename F, typename... Ts>
using single_type_conditional = std::conditional<(std::tuple_size<distinct_types_tuple<Ts...>>::value == 1), T, F>;
template<typename... Ts>
using single_type = if_single_type<typename std::tuple_element<0, std::tuple<Ts...>>::type, Ts...>;
}// namespace internal
template<typename T>
class list_type : public type_impl<std::vector<T>> {
public:
list_type(std::string name, type_handle_impl<T> element_type) : type_impl<std::vector<T>>(std::move(name)), element_type(element_type) {}
std::vector<T> parse(const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited allow_undelimited) override {
auto cur_pos = begin;
std::vector<T> values{};
bool is_delimited;
if (*cur_pos == '[') {
is_delimited = true;
cur_pos++;
} else {
if (!internal::enum_flag_contains(allow_undelimited, internal::parser_allow_undelimited::Comma)) {
throw errors::type_parsing_error(this->name, std::string(begin, end), cur_pos - begin, "expected '['");
} else {
is_delimited = false;
}
}
if (cur_pos >= end) {
throw errors::type_parsing_error(this->name, std::string(begin, end), end - begin, "unexpected end of input");
}
internal::parser_allow_undelimited sub_parse_allow_undelimited = internal::parser_allow_undelimited::Parenthesis;
if (!is_delimited) {
sub_parse_allow_undelimited &allow_undelimited;
}
while (true) {
const char *this_parse_end;
auto val = element_type->parse(cur_pos, end, this_parse_end, sub_parse_allow_undelimited);
values.push_back(val);
cur_pos = this_parse_end;
while (std::isspace(*cur_pos) && cur_pos <= end)
cur_pos++;
if (cur_pos >= end) {
if (!is_delimited) {
break;
} else {
throw errors::type_parsing_error(this->name, std::string(begin, end), end - begin, "unexpected end of input");
}
}
if (*cur_pos == ',') {
cur_pos++;
auto s = std::string_view(cur_pos, end);
auto close_bracket_pos = s.find_first_of(']');
if (s.find_first_not_of(internal::whitespace) == close_bracket_pos) {
break;
}
} else if (!is_delimited || *cur_pos == ']') {
break;
} else {
throw errors::type_parsing_error(this->name, std::string(begin, end), end - begin, "expected , or ]");
}
}
if (is_delimited) {
if (*cur_pos != ']') {
throw errors::type_parsing_error(this->name, std::string(begin, end), end - begin, "unexpected end of input");
} else {
cur_pos++;
}
}
parse_end = cur_pos;
return values;
}
private:
type_handle_impl<T> element_type;
};
class option {
public:
explicit option(std::string name) : name(std::move(name)) {}
virtual ~option() = default;
void parse(std::optional<std::string> arg, std::any &val) {
return this->do_parse(std::move(arg), val);
}
[[nodiscard]] std::string get_name() {
return name;
}
[[nodiscard]] virtual bool consumes_value() const = 0;
virtual void validate(const parse_result &res) const = 0;
protected:
const std::string name;
private:
virtual void do_parse(std::optional<std::string> arg, std::any &val) = 0;
};
using option_handle = std::shared_ptr<option>;
template<typename T>
class optional_arg : public arg, public std::enable_shared_from_this<optional_arg<T>> {
public:
optional_arg(std::string name, type_handle_impl<T> type) : arg(std::move(name)), type(type) {}
std::shared_ptr<optional_arg<T>> default_value(T val) {
default_value_ = val;
return this->shared_from_this();
}
[[nodiscard]] std::optional<T> get(const parse_result &p) const {
auto v = p.get_arg(name);
if (!v.has_value()) {
if (default_value_.has_value()) {
return default_value_.value();
}
return std::nullopt;
}
return std::make_optional(std::any_cast<T>(v));
}
[[nodiscard]] bool has(const parse_result &p) const {
return p.has_arg(name);
}
private:
type_handle_impl<T> type;
std::optional<T> default_value_ = std::nullopt;
void do_parse(std::string input, parse_result &pr) const override {
assert(!pr.get_arg(name).has_value());// an optional arg can only be parsed once
auto val = this->parse_single_value(input, type);
pr.set_arg(name, std::make_any<T>(val));
}
[[nodiscard]] bool get_can_parse_more(parse_result &pr) const override {
return !pr.get_arg(name).has_value();
}
[[nodiscard]] bool get_has_parsed_enough(parse_result &) const override {
return true;
}
};
template<typename T>
using optional_arg_handle = std::shared_ptr<optional_arg<T>>;
template<typename T>
class repeatable_arg : public arg, public std::enable_shared_from_this<repeatable_arg<T>> {
public:
repeatable_arg(std::string name, type_handle_impl<T> type) : arg(std::move(name)), type(type) {}
std::shared_ptr<repeatable_arg<T>> min(size_t min) {
min_ = min;
return this->shared_from_this();
}
std::shared_ptr<repeatable_arg<T>> max(size_t max) {
max_ = max;
return this->shared_from_this();
}
[[nodiscard]] std::vector<T> get(const parse_result &pr) const {
auto v = pr.get_arg(name);
if (!v.has_value()) {
return std::vector<T>{};
}
return std::any_cast<std::vector<T>>(v);
}
[[nodiscard]] bool has(const parse_result &pr) const {
return pr.has_arg(name);
}
private:
type_handle_impl<T> type;
std::optional<size_t> max_;
std::optional<size_t> min_;
void do_parse(std::string input, parse_result &pr) const override {
auto current_val = pr.get_arg(name);
std::vector<T> values;
if (current_val.has_value()) {
values = std::any_cast<std::vector<T>>(current_val);
}
assert(!max_.has_value() || values.size() < max_.value());
auto val = this->parse_single_value(input, type);
values.push_back(val);
pr.set_arg(name, std::make_any<std::vector<T>>(std::move(values)));
}
[[nodiscard]] bool get_can_parse_more(parse_result &pr) const override {
if (max_.has_value()) {
auto val = pr.get_arg(name);
if (!val.has_value()) {
return true;
}
return std::any_cast<std::vector<T>>(val).size() < max_.value();
} else {
return true;
}
}
[[nodiscard]] bool get_has_parsed_enough(parse_result &pr) const override {
if (min_.has_value() && min_.value() > 0) {
auto val = pr.get_arg(name);
if (!val.has_value()) {
return false;
}
auto val_ = std::any_cast<std::vector<T>>(val);
return val_.size() >= min_.value();
} else {
return true;
}
}
};
template<typename T>
using repeatable_arg_handle = std::shared_ptr<repeatable_arg<T>>;
class repeatable_flag_impl : public option, public std::enable_shared_from_this<repeatable_flag_impl> {
public:
repeatable_flag_impl(std::string name) : option(std::move(name)) {}
std::shared_ptr<repeatable_flag_impl> min(unsigned int min) {
min_ = min;
return this->shared_from_this();
}
std::shared_ptr<repeatable_flag_impl> max(unsigned int max) {
max_ = max;
return this->shared_from_this();
}
[[nodiscard]] bool consumes_value() const override {
return false;
}
void validate(const parse_result &pr) const override {
unsigned int count = get(pr);
if ((min_.has_value() && count < min_.value()) ||
(max_.has_value() && count > max_.value())) {
throw errors::wrong_option_count_error(this->name, min_, max_, count);
}
}
[[nodiscard]] unsigned int get(const parse_result &pr) const {
auto v = pr.get_opt(name);
if (!v.has_value()) {
return 0;
}
return std::any_cast<unsigned int>(v);
}
[[nodiscard]] bool has(const parse_result &pr) const {
return pr.has_opt(name);
}
private:
void do_parse(std::optional<std::string> arg, std::any &val) override {
assert(!arg.has_value());
unsigned int count = 0;
if (val.has_value()) {
count = std::any_cast<unsigned int>(val);
}
val = std::make_any<unsigned int>(count + 1);
}
std::optional<unsigned int> min_ = std::nullopt;
std::optional<unsigned int> max_ = std::nullopt;
};
using repeatable_flag_handle_impl = std::shared_ptr<repeatable_flag_impl>;
template<typename T>
class repeatable_option_impl : public option, public std::enable_shared_from_this<repeatable_option_impl<T>> {
public:
repeatable_option_impl(std::string name, type_handle_impl<T> type) : option(std::move(name)), type(std::move(type)) {}
std::shared_ptr<repeatable_option_impl<T>> default_value(std::vector<T> val) {
this->default_value_ = std::make_optional(val);
return this->shared_from_this();
}
std::shared_ptr<repeatable_option_impl<T>> min(unsigned int min) {
this->min_ = min;
return this->shared_from_this();
}
std::shared_ptr<repeatable_option_impl<T>> max(unsigned int max) {
this->max_ = max;
return this->shared_from_this();
}
void validate(const parse_result &pr) const override {
unsigned int count = 0;
if (pr.has_opt(this->name)) {
count = get(pr).size();
}
if ((min_.has_value() && count < min_.value()) ||
(max_.has_value() && count > max_.value())) {
throw errors::wrong_option_count_error(this->name, min_, max_, count);
}
}
[[nodiscard]] bool consumes_value() const override {
return true;
}
[[nodiscard]] std::vector<T> get(const parse_result &pr) const {
auto v = pr.get_opt(name);
if (!v.has_value()) {
if (default_value_.has_value()) {
return default_value_.value();
}
throw std::runtime_error(std::format("option {} was not provided", name));
}
return std::any_cast<std::vector<T>>(v);
}
[[nodiscard]] bool has(const parse_result &pr) const {
return pr.has_opt(name);
}
private:
void do_parse(std::optional<std::string> arg, std::any &val) override {
assert(arg.has_value());
const char *parse_end;
auto begin = &*arg->begin();
auto end = &*arg->end();
auto single_val = this->type->parse(begin, end, parse_end, internal::parser_allow_undelimited::Any);
if (parse_end != end) {
throw errors::type_parsing_error(this->type->get_name(), std::string(begin, end), parse_end - begin, "unexpected input");
}
std::vector<T> val_vec{};
if (val.has_value()) {
val_vec = std::any_cast<std::vector<T>>(val);
}
val_vec.push_back(single_val);
val = std::make_any<std::vector<T>>(val_vec);
}
type_handle_impl<T> type;
std::optional<std::vector<T>> default_value_ = std::nullopt;
std::optional<unsigned int> min_ = std::nullopt;
std::optional<unsigned int> max_ = std::nullopt;
};
template<typename T>
using repeatable_option_handle_impl = std::shared_ptr<repeatable_option_impl<T>>;
template<typename T>
class single_arg : public arg, public std::enable_shared_from_this<single_arg<T>> {
public:
single_arg(std::string name, type_handle_impl<T> type) : arg(std::move(name)), type(type) {}
[[nodiscard]] T get(const parse_result &pr) const {
auto v = pr.get_arg(name);
if (!v.has_value()) {
throw errors::missing_argument_error(name);
}
return std::any_cast<T>(v);
}
[[nodiscard]] bool has(const parse_result &pr) const {
return pr.has_arg(name);
}
private:
type_handle_impl<T> type;
void do_parse(std::string input, parse_result &pr) const override {
assert(!pr.get_arg(name).has_value());// a single arg can only be parsed once
auto val = this->parse_single_value(input, type);
pr.set_arg(name, std::make_any<T>(val));
}
bool get_can_parse_more(parse_result &pr) const override {
return !pr.get_arg(name).has_value();
}
bool get_has_parsed_enough(parse_result &pr) const override {
return pr.get_arg(name).has_value();
}
};
template<typename T>
using single_arg_handle = std::shared_ptr<single_arg<T>>;
class flag_impl : public option, public std::enable_shared_from_this<flag_impl> {
public:
explicit flag_impl(std::string name) : option(std::move(name)) {}
[[nodiscard]] bool is_inverted() const {
return is_inverted_;
}
std::shared_ptr<flag_impl> invert() {
this->is_inverted_ = true;
return this->shared_from_this();
}
[[nodiscard]] bool consumes_value() const override {
return false;
}
void validate(const parse_result &) const override {
}
[[nodiscard]] bool get(const parse_result &pr) const {
bool val = pr.has_opt(name);
if (is_inverted_) {
val = !val;
}
return val;
}
[[nodiscard]] bool has(const parse_result &pr) const {
return pr.has_opt(name);
}
private:
void do_parse(std::optional<std::string> arg, std::any &val) override {
assert(!arg.has_value());
if (!val.has_value()) {
val = std::make_any<bool>(true);
}
}
bool is_inverted_{};
};
using flag_handle_impl = std::shared_ptr<flag_impl>;
template<typename T>
class option_impl : public option, public std::enable_shared_from_this<option_impl<T>> {
public:
option_impl(std::string name, type_handle_impl<T> type) : option(std::move(name)), type(std::move(type)) {}
std::shared_ptr<option_impl<T>> required() {
required_ = true;
return this->shared_from_this();
}
std::shared_ptr<option_impl<T>> default_value(T val) {
this->default_value_ = val;
this->has_default_value = true;
return this->shared_from_this();
}
void validate(const parse_result &res) const override {
if (this->required_) {
if (!res.has_opt(this->name)) {
throw errors::missing_option_error(this->name);
}
}
}
[[nodiscard]] bool consumes_value() const override {
return true;
}
[[nodiscard]] T get(const parse_result &pr) const {
auto v = pr.get_opt(name);
if (!v.has_value()) {
if (has_default_value) {
return default_value_;
}
throw std::runtime_error(std::format("option {} was not provided", name));
}
return std::any_cast<T>(v);
}
[[nodiscard]] bool has(const parse_result &pr) const {
return pr.has_opt(name);
}
private:
void do_parse(std::optional<std::string> arg, std::any &val) override {
assert(arg.has_value());
const char *parse_end;
auto begin = &*arg->begin();
auto end = &*arg->end();
val = this->type->parse(begin, end, parse_end, internal::parser_allow_undelimited::Any);
if (parse_end != end) {
throw errors::type_parsing_error(this->type->get_name(), std::string(begin, end), parse_end - begin, "unexpected input");
}
}
type_handle_impl<T> type;
bool required_ = false;
bool has_default_value = false;
T default_value_;
};
template<typename T>
using option_handle_impl = std::shared_ptr<option_impl<T>>;
// adapted from https://blog.tartanllama.xyz/exploding-tuples-fold-expressions/
namespace internal::tuple_foreach_impl {
template<std::size_t... Idx>
auto make_index_dispatcher(std::index_sequence<Idx...>) {
return [](auto &&f) { (f(std::integral_constant<std::size_t, Idx>{}), ...); };
}
template<std::size_t N>
auto make_index_dispatcher() {
return make_index_dispatcher(std::make_index_sequence<N>{});
}
}// namespace internal::tuple_foreach_impl
namespace internal {
template<typename Tuple, typename Func>
void tuple_foreach(Tuple &&t, Func &&f) {
constexpr auto n = std::tuple_size<std::decay_t<Tuple>>::value;
auto dispatcher = internal::tuple_foreach_impl::make_index_dispatcher<n>();
dispatcher([&f, &t](auto idx) { f(std::get<idx>(std::forward<Tuple>(t)), idx); });
}
}// namespace internal
template<typename R, typename... Ts>
class base_tuple_type : public type_impl<R> {
public:
base_tuple_type(std::string name, std::tuple<type_handle_impl<Ts>...> types) : type_impl<R>(std::move(name)), types(types) {}
protected:
void parse_into_tuple(const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited allow_undelimited, std::tuple<Ts...> &result) {
auto cur_pos = begin;
size_t tuple_size = std::tuple_size_v<std::tuple<Ts...>>;
bool requires_delimiter = tuple_size > 1 && !internal::enum_flag_contains(allow_undelimited, internal::parser_allow_undelimited::Comma);
bool is_delimited;
if (std::string_view(begin, end).starts_with(this->name)) {
cur_pos += this->name.size();
requires_delimiter = true;
}
if (*cur_pos == '(') {
is_delimited = true;
cur_pos++;
} else if (requires_delimiter) {
throw errors::type_parsing_error(this->name, std::string(begin, end), cur_pos - begin, "expected '('");
} else {
is_delimited = false;
}
internal::parser_allow_undelimited sub_parse_allow_undelimited = internal::parser_allow_undelimited::Brackets;
if (tuple_size == 1) {
sub_parse_allow_undelimited = sub_parse_allow_undelimited | internal::parser_allow_undelimited::Comma;
}
if (!is_delimited) {
sub_parse_allow_undelimited = sub_parse_allow_undelimited & allow_undelimited;
}
internal::tuple_foreach(types, [&cur_pos, &end, &result, this, &begin, &sub_parse_allow_undelimited]<typename T>(type_handle_impl<T> &type, auto idx) {
const char *this_parse_end;
std::get<idx>(result) = type->parse(cur_pos, end, this_parse_end, sub_parse_allow_undelimited);
cur_pos = this_parse_end;
while (std::isspace(*cur_pos) && cur_pos < end)
cur_pos++;
if (idx < std::tuple_size_v<std::tuple<Ts...>> - 1) {
if (*cur_pos == ',') {
cur_pos += 1;
} else {
throw errors::type_parsing_error(this->name, std::string(begin, end), cur_pos - begin, "expected ','");
}
}
});
if (*cur_pos == ')') {
if (is_delimited) {
cur_pos++;
}
} else {
if (is_delimited) {
throw errors::type_parsing_error(this->name, std::string(begin, end), cur_pos - begin, "expected ')'");
}
}
parse_end = cur_pos;
}
private:
std::tuple<type_handle_impl<Ts>...>
types;
};
template<typename... Ts>
class tuple_type : public base_tuple_type<std::tuple<Ts...>, Ts...> {
public:
tuple_type(std::string name, std::tuple<type_handle_impl<Ts>...> types) : base_tuple_type<std::tuple<Ts...>, Ts...>(std::move(name), types) {}
std::tuple<Ts...> parse(const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited allow_undelimited) override {
std::tuple<Ts...> result{};
this->parse_into_tuple(begin, end, parse_end, allow_undelimited, result);
return result;
}
};
template<typename R, typename... Ts>
class custom_tuple_type : public base_tuple_type<R, Ts...> {
public:
custom_tuple_type(std::string name, std::tuple<type_handle_impl<Ts>...> types) : base_tuple_type<R, Ts...>(std::move(name), types) {}
R parse(const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited allow_undelimited) override {
std::tuple<Ts...> result{};
this->parse_into_tuple(begin, end, parse_end, allow_undelimited, result);
return std::apply(construct_return_type, result);
}
private:
static R construct_return_type(Ts... args) {
return R(args...);
}
};
template<typename R, typename... Ts>
class custom_tuple_type_with_constructor : public base_tuple_type<R, Ts...> {
public:
custom_tuple_type_with_constructor(std::string name, std::tuple<type_handle_impl<Ts>...> types, std::function<R(Ts...)> constructor)
: base_tuple_type<R, Ts...>(std::move(name), types), constructor(constructor) {}
R parse(const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited allow_undelimited) override {
std::tuple<Ts...> result{};
this->parse_into_tuple(begin, end, parse_end, allow_undelimited, result);
return std::apply(constructor, result);
}
private:
std::function<R(Ts...)> constructor;
};
namespace internal {
template<typename... Ts>
std::string construct_string_type_name_list_from_tuple(std::tuple<Ts...> types) {
std::string expected_types;
expected_types = std::get<0>(types)->get_name();
if (std::tuple_size_v < decltype(types) >> 1) {
internal::tuple_foreach(types, [&expected_types]<typename T>(type_handle_impl<T> &type, auto idx) {
if (idx == std::tuple_size_v<decltype(types)> - 1 || idx == 0) {
return;
}
expected_types += ", " + type->get_name();
});
expected_types += " or " + std::get<std::tuple_size_v<decltype(types)> - 1>(types)->get_name();
}
return expected_types;
}
}// namespace internal
template<typename... Ts>
class union_type;
template<typename... Ts>
requires(std::tuple_size<internal::distinct_types_tuple<Ts...>>::value > 1)
class union_type<Ts...> : public type_impl<internal::distinct_types_variant<Ts...>> {
using variant_type = internal::distinct_types_variant<Ts...>;
public:
union_type(std::string name, std::tuple<type_handle_impl<Ts>...> types) : type_impl<variant_type>(std::move(name)), types(std::move(types)) {}
variant_type parse(const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited allow_undelimited) override {
std::vector<variant_type> results{};
std::vector<std::string> result_types{};
size_t length = 0;
internal::tuple_foreach(types, [&begin, &end, &length, &results, &result_types, &allow_undelimited]<typename T>(type_handle_impl<T> &type, auto) {
const char *this_parse_end;
try {
auto val = type->parse(begin, end, this_parse_end, allow_undelimited);
size_t this_length = this_parse_end - begin;
if (this_length >= length) {
variant_type variant_wrapped_val = val;
if (this_length == length) {
results.push_back(variant_wrapped_val);
result_types.push_back(type->get_name());
} else {
length = this_length;
results = {variant_wrapped_val};
result_types = {type->get_name()};
}
}
} catch (errors::type_parsing_error &e) {
// ignore errors
}
});
if (length == 0) {
std::string expected_types = internal::construct_string_type_name_list_from_tuple(types);
throw errors::type_parsing_error(this->name, std::string(begin, end), 0, std::format("expected {}", expected_types));
}
if (results.size() > 1) {
throw errors::ambiguous_parse_error(this->name, std::string(begin, end), 0, result_types);
}
parse_end = begin + length;
return results[0];
}
private:
std::tuple<type_handle_impl<Ts>...> types;
};
template<typename T, typename... Ts>
requires(std::tuple_size<internal::distinct_types_tuple<T, Ts...>>::value == 1)
class union_type<T, Ts...> : public type_impl<T> {
public:
union_type(std::string name, std::tuple<type_handle_impl<T>, type_handle_impl<Ts>...> types) : type_impl<T>(std::move(name)), types(std::move(types)) {}
T parse(const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited) override {
std::vector<T> results{};
std::vector<std::string> result_types{};
size_t length = 0;
internal::tuple_foreach(types, [&begin, &end, &length, &results, &result_types](type_handle_impl<T> &type, auto) {
const char *this_parse_end;
try {
auto val = type->parse(begin, end, this_parse_end);
size_t this_length = this_parse_end - begin;
if (this_length >= length) {
if (this_length == length) {
results.push_back(val);
result_types.push_back(type->get_name());
} else {
length = this_length;
results = {val};
result_types = {type->get_name()};
}
}
} catch (errors::type_parsing_error &e) {
// ignore errors
}
});
if (length == 0) {
std::string expected_types = internal::construct_string_type_name_list_from_tuple(types);
throw errors::type_parsing_error(this->name, std::string(begin, end), 0, std::format("expected {}", expected_types));
}
if (results.size() > 1 && !std::equal(results.begin() + 1, results.end(), results.begin())) {
throw errors::ambiguous_parse_error(this->name, std::string(begin, end), 0, result_types);
}
parse_end = begin + length;
return results[0];
}
private:
std::tuple<type_handle_impl<T>, type_handle_impl<Ts>...> types;
};
// TODO: help output
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