#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 #include #include #include namespace argparser { namespace internal { template std::string construct_string_type_name_list_from_tuple(std::tuple types) { std::string expected_types; expected_types = std::get<0>(types)->get_name(); if (std::tuple_size_v > 1) { internal::tuple_foreach(types, [&expected_types](type_handle_impl &type, auto idx) { if (idx == std::tuple_size_v - 1 || idx == 0) { return; } expected_types += ", " + type->get_name(); }); expected_types += " or " + std::get - 1>(types)->get_name(); } return expected_types; } }// namespace internal template class union_type; template requires(std::tuple_size>::value > 1) class union_type : public type_impl> { using variant_type = internal::distinct_types_variant; public: union_type(std::string name, std::tuple...> types) : type_impl(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 results{}; std::vector result_types{}; size_t length = 0; internal::tuple_foreach(types, [&begin, &end, &length, &results, &result_types, &allow_undelimited](type_handle_impl &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...> types; }; template requires(std::tuple_size>::value == 1) class union_type : public type_impl { public: union_type(std::string name, std::tuple, type_handle_impl...> types) : type_impl(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 results{}; std::vector result_types{}; size_t length = 0; internal::tuple_foreach(types, [&begin, &end, &length, &results, &result_types](type_handle_impl &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...> types; }; }// namespace argparser #endif//ARGPARSER_UNION_TYPE_H