userver: userver/formats/parse/variant.hpp Source File
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
variant.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/formats/parse/variant.hpp
4/// @brief Ineffective but generic parser for std::variant type
5///
6/// Parsing is performed for each of the N alternatives in variant, N-1
7/// exceptions is thrown and caught during the parsing.
8///
9/// @ingroup userver_universal userver_formats_parse
10
11#include <optional>
12#include <typeinfo>
13#include <variant>
14
15#include <fmt/format.h>
16
17#include <userver/compiler/demangle.hpp>
18#include <userver/formats/parse/to.hpp>
19
20USERVER_NAMESPACE_BEGIN
21
22namespace formats::json {
23class Value;
24}
25
26namespace formats::parse {
27
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 '{}'",
32 path,
33 compiler::GetTypeName<Variant>(),
34 compiler::GetTypeName<TypeA>(),
36 ));
37}
38
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>()));
42}
43
44namespace impl {
45template <class T, class Value, typename Result>
46void ParseVariantSingle(const Value& value, std::optional<Result>& result) {
47 if (result) {
48 const auto old_type = std::visit([](const auto& v) -> std::type_index { return typeid(v); }, *result);
49 try {
50 value.template As<T>();
51 } catch (const std::exception&) {
52 return;
53 }
54
55 ThrowVariantAmbiguousParse<typename Value::ParseException, Result, T>(value.GetPath(), old_type);
56 } else {
57 // No result yet
58 try {
59 result = value.template As<T>();
60 } catch (const std::exception&) {
61 }
62 }
63}
64
65} // namespace impl
66
67template <class Value, typename... Types>
68std::variant<decltype(Parse(std::declval<Value>(), To<Types>{}))...>
69Parse(const Value& value, formats::parse::To<std::variant<Types...>>) {
70 std::optional<std::variant<decltype(Parse(std::declval<Value>(), To<Types>{}))...>> result;
71
72 if constexpr (std::is_same_v<Value, formats::json::Value>) {
73 Value value2{value};
74 value2.DropRootPath();
75 (impl::ParseVariantSingle<Types>(value2, result), ...);
76 } else {
77 (impl::ParseVariantSingle<Types>(value, result), ...);
78 }
79
80 if (!result) {
81 ThrowVariantParseException<typename Value::ParseException, std::variant<Types...>>(value.GetPath());
82 }
83
84 return std::move(*result);
85}
86
87} // namespace formats::parse
88
89USERVER_NAMESPACE_END