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

128 lines
5.2 KiB
C++

#ifndef ARGPARSER_TUPLE_TYPE_H
#define ARGPARSER_TUPLE_TYPE_H
#include "errors.h"
#include "tuple-iteration.h"
#include "type.h"
#include <any>
#include <functional>
#include <string>
#include <tuple>
namespace argparser {
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 argparser
#endif//ARGPARSER_TUPLE_TYPE_H