15#include <fmt/format.h>
17#include <userver/compiler/demangle.hpp>
18#include <userver/formats/parse/to.hpp>
20USERVER_NAMESPACE_BEGIN
28template <
typename ParseException,
typename Variant,
typename TypeA>
29[[noreturn]]
void ThrowVariantAmbiguousParse(std::string_view path, std::type_index type_b) {
30 throw ParseException(fmt::format(
31 "Value of '{}' is ambiguous, it is parseable into multiple variants of '{}', at least '{}' and '{}'",
33 compiler::GetTypeName<Variant>(),
34 compiler::GetTypeName<TypeA>(),
35 compiler::GetTypeName(type_b)
39template <
class ParseException,
typename Variant>
40[[noreturn]]
void ThrowVariantParseException(std::string_view path) {
41 throw ParseException(fmt::format(
"Value of '{}' cannot be parsed as {}", path, compiler::GetTypeName<Variant>()));
45template <
class T,
class Value,
typename Result>
46void ParseVariantSingle(
const Value& value, std::optional<Result>& result) {
48 const auto old_type = std::visit([](
const auto& v) -> std::type_index {
return typeid(v); }, *result);
50 value.
template As<T>();
51 }
catch (
const std::exception&) {
55 ThrowVariantAmbiguousParse<
typename Value::ParseException, Result, T>(value.GetPath(), old_type);
59 result = value.
template As<T>();
60 }
catch (
const std::exception&) {
67template <
class Value,
typename... Types>
68auto Parse(
const Value& value,
formats::
parse::
To<std::variant<Types...>>) {
69 std::optional<std::variant<
decltype(Parse(std::declval<Value>(),
To<Types>{}))...>> result;
71 if constexpr (std::is_same_v<Value,
formats::
json::Value>) {
73 value2.DropRootPath();
74 (impl::ParseVariantSingle<Types>(value2, result), ...);
76 (impl::ParseVariantSingle<Types>(value, result), ...);
80 ThrowVariantParseException<
typename Value::ParseException, std::variant<Types...>>(value.GetPath());
83 return std::move(*result);