userver: userver/formats/parse/common_containers.hpp Source File
Loading...
Searching...
No Matches
common_containers.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/formats/parse/common_containers.hpp
4/// @brief Parsers and converters for Standard Library containers and
5/// std::optional
6///
7/// @ingroup userver_universal userver_formats_parse
8
9#include <optional>
10#include <string_view>
11#include <type_traits>
12
13#include <userver/formats/common/meta.hpp>
14#include <userver/formats/parse/to.hpp>
15#include <userver/utils/meta.hpp>
16
17/// @brief Boost.UUID helpers referenced by format parsers and serializers.
18namespace boost::uuids {
19struct uuid;
20}
21
22USERVER_NAMESPACE_BEGIN
23
24namespace utils::impl::strong_typedef {
25struct StrongTypedefTag;
26}
27
28namespace formats::parse {
29
30namespace impl {
31
32template <typename T, class Value>
33inline T AsExtractor(const Value& value) {
34 return value.template As<T>();
35}
36
37template <typename T, class Value>
38inline T ConvertToExtractor(const Value& value) {
39 return value.template ConvertTo<T>();
40}
41
42template <typename ArrayType, class Value, typename ExtractFunc>
43ArrayType ParseArray(const Value& value, ExtractFunc extract_func) {
44 value.CheckArrayOrNull();
45 ArrayType response;
46 auto inserter = std::inserter(response, response.end());
47
48 for (const auto& subitem : value) {
49 *inserter = extract_func(subitem);
50 ++inserter;
51 }
52
53 return response;
54}
55
56template <typename ObjectType, class Value, typename ExtractFunc>
57ObjectType ParseObject(const Value& value, ExtractFunc extract_func) {
58 using KeyType = typename ObjectType::key_type;
59
60 value.CheckObjectOrNull();
61 ObjectType result;
62
63 for (auto it = value.begin(); it != value.end(); ++it) {
64 if constexpr (std::is_constructible_v<KeyType, std::string>) {
65 result.emplace(it.GetName(), extract_func(*it));
66 } else {
67 result.emplace(Parse(std::string_view(it.GetName()), To<KeyType>{}), extract_func(*it));
68 }
69 }
70
71 return result;
72}
73
74template <typename T>
75concept RangeNotMap =
76 meta::kIsRange<T> && !meta::kIsMap<T> && !std::is_same_v<T, boost::uuids::uuid> &&
77 !std::is_convertible_v<T&, utils::impl::strong_typedef::StrongTypedefTag&>;
78
79} // namespace impl
80
81template <impl::RangeNotMap T, common::kIsFormatValue Value>
82T Parse(const Value& value, To<T>) {
83 return impl::ParseArray<T>(value, &impl::AsExtractor<meta::RangeValueType<T>, Value>);
84}
85
86template <meta::kIsMap T, common::kIsFormatValue Value>
87T Parse(const Value& value, To<T>) {
88 return impl::ParseObject<T>(value, &impl::AsExtractor<typename T::mapped_type, Value>);
89}
90
91template <typename T, typename Value>
92std::optional<decltype(Parse(std::declval<Value>(), To<T>{}))> Parse(const Value& value, To<std::optional<T>>) {
93 if (value.IsMissing() || value.IsNull()) {
94 return std::nullopt;
95 }
96 return value.template As<T>();
97}
98
99template <class Value>
100std::optional<std::nullptr_t> Parse(const Value&, To<std::optional<std::nullptr_t>>) {
101 static_assert(!sizeof(Value), "optional<nullptr_t> is forbidden, check IsNull() instead");
102 return nullptr;
103}
104
105template <impl::RangeNotMap T, typename Value>
106T Convert(const Value& value, To<T>) {
107 if (value.IsMissing()) {
108 return {};
109 }
110 return impl::ParseArray<T>(value, &impl::ConvertToExtractor<meta::RangeValueType<T>, Value>);
111}
112
113template <meta::kIsMap T, typename Value>
114T Convert(const Value& value, To<T>) {
115 if (value.IsMissing()) {
116 return {};
117 }
118 return impl::ParseObject<T>(value, &impl::ConvertToExtractor<typename T::mapped_type, Value>);
119}
120
121template <typename T, typename Value>
122std::optional<T> Convert(const Value& value, To<std::optional<T>>) {
123 if (value.IsMissing() || value.IsNull()) {
124 return std::nullopt;
125 }
126 return value.template ConvertTo<T>();
127}
128
129template <class Value>
130std::optional<std::nullptr_t> Convert(const Value&, To<std::optional<std::nullptr_t>>) {
131 static_assert(!sizeof(Value), "optional<nullptr_t> is forbidden, check IsNull() instead");
132 return nullptr;
133}
134
135} // namespace formats::parse
136
137USERVER_NAMESPACE_END