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, std::type_index type_b) {
28 throw ParseException(
29 "Value of '" + path + "' is ambiguous, it is parseable into multiple variants of '" +
30 compiler::GetTypeName<Variant>() + "', at least '" + compiler::GetTypeName<TypeA>() + "' and '" +
31 compiler::GetTypeName(type_b) + "'"
32 );
33}
34
35template <class ParseException, typename Variant>
36[[noreturn]] void ThrowVariantParseException(const std::string& path) {
37 throw ParseException("Value of '" + path + "' cannot be parsed as " + compiler::GetTypeName<Variant>());
38}
39
40namespace impl {
41template <class T, class Value, typename Result>
42void ParseVariantSingle(const Value& value, std::optional<Result>& result) {
43 if (result) {
44 const auto old_type = std::visit([](const auto& v) -> std::type_index { return typeid(v); }, *result);
45 try {
46 value.template As<T>();
47 } catch (const std::exception&) {
48 return;
49 }
50
51 ThrowVariantAmbiguousParse<typename Value::ParseException, Result, T>(value.GetPath(), old_type);
52 } else {
53 // No result yet
54 try {
55 result = value.template As<T>();
56 } catch (const std::exception&) {
57 }
58 }
59}
60
61} // namespace impl
62
63template <class Value, typename... Types>
64auto Parse(const Value& value, formats::parse::To<std::variant<Types...>>) {
65 std::optional<std::variant<decltype(Parse(std::declval<Value>(), To<Types>{}))...>> result;
66
67 if constexpr (std::is_same_v<Value, formats::json::Value>) {
68 Value value2{value};
69 value2.DropRootPath();
70 (impl::ParseVariantSingle<Types>(value2, result), ...);
71 } else {
72 (impl::ParseVariantSingle<Types>(value, result), ...);
73 }
74
75 if (!result) {
76 ThrowVariantParseException<typename Value::ParseException, std::variant<Types...>>(value.GetPath());
77 }
78
79 return std::move(*result);
80}
81
82} // namespace formats::parse
83
84USERVER_NAMESPACE_END