userver: userver/formats/parse/variant.hpp Source File
Loading...
Searching...
No Matches
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