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

132 lines
5.7 KiB
C++

#ifndef ARGPARSER_UNION_TYPE_H
#define ARGPARSER_UNION_TYPE_H
#include "distinct-types.h"
#include "errors.h"
#include "tuple-iteration.h"
#include "type.h"
#include <any>
#include <string>
#include <tuple>
#include <vector>
namespace argparser {
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;
};
}// namespace argparser
#endif//ARGPARSER_UNION_TYPE_H