userver: userver/formats/parse/variant.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
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 <userver/compiler/demangle.hpp>
16#include <userver/formats/parse/to.hpp>
17
18USERVER_NAMESPACE_BEGIN
19
20namespace formats::json {
21class Value;
22}
23
24namespace formats::parse {
25
26template <typename ParseException, typename Variant, typename TypeA>
27[[noreturn]] void ThrowVariantAmbiguousParse(const std::string& path,
28 std::type_index type_b) {
29 throw ParseException(
30 "Value of '" + path +
31 "' is ambiguous, it is parseable into multiple variants of '" +
32 compiler::GetTypeName<Variant>() + "', at least '" +
33 compiler::GetTypeName<TypeA>() + "' and '" +
34 compiler::GetTypeName(type_b) + "'");
35}
36
37template <class ParseException, typename Variant>
38[[noreturn]] void ThrowVariantParseException(const std::string& path) {
39 throw ParseException("Value of '" + path + "' cannot be parsed as " +
40 compiler::GetTypeName<Variant>());
41}
42
43namespace impl {
44template <class T, class Value, typename Result>
45void ParseVariantSingle(const Value& value, std::optional<Result>& result) {
46 if (result) {
47 const auto old_type = std::visit(
48 [](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>(
56 value.GetPath(), old_type);
57 } else {
58 // No result yet
59 try {
60 result = value.template As<T>();
61 } catch (const std::exception&) {
62 }
63 }
64}
65
66} // namespace impl
67
68template <class Value, typename... Types>
69std::variant<Types...> Parse(const Value& value,
70 formats::parse::To<std::variant<Types...>>) {
71 std::optional<std::variant<Types...>> result;
72
73 if constexpr (std::is_same_v<Value, formats::json::Value>) {
74 Value value2{value};
75 value2.DropRootPath();
76 (impl::ParseVariantSingle<Types>(value2, result), ...);
77 } else {
78 (impl::ParseVariantSingle<Types>(value, result), ...);
79 }
80
81 if (!result) {
82 ThrowVariantParseException<typename Value::ParseException,
83 std::variant<Types...>>(value.GetPath());
84 }
85
86 return std::move(*result);
87}
88
89} // namespace formats::parse
90
91USERVER_NAMESPACE_END