mirror of
https://git.lynn.is/Gwen/argparser.git
synced 2024-04-29 14:43:46 +02:00
132 lines
5.7 KiB
C++
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
|