userver: userver/storages/postgres/io/enum_types.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
enum_types.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/io/enum_types.hpp
4/// @brief Enum I/O support
5/// @ingroup userver_postgres_parse_and_format
6
7#include <string_view>
8#include <unordered_map>
9
10#include <userver/storages/postgres/io/buffer_io.hpp>
11#include <userver/storages/postgres/io/buffer_io_base.hpp>
12#include <userver/storages/postgres/io/pg_types.hpp>
13#include <userver/storages/postgres/io/type_mapping.hpp>
14#include <userver/storages/postgres/io/user_types.hpp>
15#include <userver/utils/trivial_map_fwd.hpp>
16
17#include <userver/storages/postgres/detail/string_hash.hpp>
18
19USERVER_NAMESPACE_BEGIN
20
21namespace storages::postgres::io {
22
23namespace detail {
24
25/// Enumerator literal value holder
26template <typename Enum>
27struct Enumerator {
28 Enum enumerator;
29 std::string_view literal;
30
31 constexpr Enumerator(Enum en, std::string_view lit)
32 : enumerator{en}, literal{lit} {}
33};
34
35} // namespace detail
36
37/// Optional base template for providing type aliases for defining enumeration
38/// mapping.
39template <typename Enum>
41 static_assert(std::is_enum<Enum>(), "Type must be an enumeration");
42
43 /// @brief Type alias for the enumeration.
44 ///
45 /// As the mapping must be specialized in `storages::postgres::io` namespace,
46 /// the enumerator value can be quite a long name. This type alias is a
47 /// shortcut to the enumeration type.
48 using EnumType = Enum;
49
50 /// Type alias for enumerator literal value holder
51 using Enumerator = detail::Enumerator<Enum>;
52
53 /// @brief Type alias for enumerator-literal mapping.
54 ///
55 /// See @ref pg_enum
56 using EnumeratorList = const std::initializer_list<Enumerator>;
57};
58
59namespace detail {
60
61template <typename Enum, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
62struct AreEnumeratorsDefined : std::false_type {};
63
64template <typename Enum>
65struct AreEnumeratorsDefined<
66 Enum,
67 USERVER_NAMESPACE::utils::void_t<decltype(CppToUserPg<Enum>::enumerators)*>>
68 : std::true_type {};
69
70template <typename Enum>
71struct Enumerators {
72 static_assert(std::is_enum<Enum>(), "Type must be an enumeration");
73 static_assert(AreEnumeratorsDefined<Enum>(),
74 "CppToUserPg for an enumeration must contain a static "
75 "`enumerators` member of `utils::TrivialBiMap` type or "
76 "`storages::postgres::io::detail::Enumerator[]`");
77 using Type = decltype(CppToUserPg<Enum>::enumerators);
78};
79
80template <typename Enum, typename = typename Enumerators<Enum>::Type>
81class EnumerationMap;
82
83template <typename Enum, typename T>
84class EnumerationMap {
85 using StringType = std::string_view;
86 using EnumType = Enum;
87 using MappingType = CppToUserPg<EnumType>;
88 using EnumeratorType = Enumerator<EnumType>;
89
90 struct EnumHash {
91 std::size_t operator()(EnumType v) const {
92 using underlying_type = std::underlying_type_t<EnumType>;
93 using hasher = std::hash<underlying_type>;
94 return hasher{}(static_cast<underlying_type>(v));
95 }
96 };
97
98 using EnumToString = std::unordered_map<EnumType, StringType, EnumHash>;
99 using StringToEnum =
100 std::unordered_map<StringType, EnumType, utils::StringViewHash>;
101 using MapsPair = std::pair<EnumToString, StringToEnum>;
102
103 static MapsPair MapLiterals() {
104 MapsPair maps;
105 for (const auto& enumerator : enumerators) {
106 maps.first.insert(
107 std::make_pair(enumerator.enumerator, enumerator.literal));
108 maps.second.insert(
109 std::make_pair(enumerator.literal, enumerator.enumerator));
110 }
111 return maps;
112 }
113 static const MapsPair& GetMaps() {
114 static auto maps_ = MapLiterals();
115 return maps_;
116 }
117 static const EnumToString& EnumeratorMap() { return GetMaps().first; }
118 static const StringToEnum& LiteralMap() { return GetMaps().second; }
119
120 public:
121 static constexpr const auto& enumerators = MappingType::enumerators;
122 static constexpr std::size_t size = std::size(enumerators);
123 static EnumType GetEnumerator(StringType literal) {
124 static const auto& map = LiteralMap();
125 if (auto f = map.find(literal); f != map.end()) {
126 return f->second;
127 }
128 throw InvalidEnumerationLiteral{
129 compiler::GetTypeName<EnumType>(),
130 std::string{literal.data(), literal.size()}};
131 }
132 static StringType GetLiteral(EnumType enumerator) {
133 static const auto& map = EnumeratorMap();
134 if (auto f = map.find(enumerator); f != map.end()) {
135 return f->second;
136 }
137 throw InvalidEnumerationValue{enumerator};
138 }
139};
140
141template <typename Enum, typename Func>
142class EnumerationMap<Enum, const USERVER_NAMESPACE::utils::TrivialBiMap<Func>> {
143 using StringType = std::string_view;
144 using EnumType = Enum;
145 using MappingType = CppToUserPg<EnumType>;
146
147 public:
148 static constexpr const auto& enumerators = MappingType::enumerators;
149 static constexpr std::size_t size = enumerators.size();
150 static constexpr EnumType GetEnumerator(StringType literal) {
151 auto enumerator = enumerators.TryFind(literal);
152 if (enumerator.has_value()) return *enumerator;
153 throw InvalidEnumerationLiteral{
154 compiler::GetTypeName<EnumType>(),
155 std::string{literal.data(), literal.size()}};
156 }
157 static constexpr StringType GetLiteral(EnumType enumerator) {
158 auto literal = enumerators.TryFind(enumerator);
159 if (literal.has_value()) return *literal;
160 throw InvalidEnumerationValue{enumerator};
161 }
162};
163
164template <typename Enum>
165struct EnumParser : BufferParserBase<Enum> {
166 using BaseType = BufferParserBase<Enum>;
167 using EnumMap = EnumerationMap<Enum>;
168
169 using BaseType::BaseType;
170
171 void operator()(const FieldBuffer& buffer) {
172 std::string_view literal;
173 io::ReadBuffer(buffer, literal);
174 this->value = EnumMap::GetEnumerator(literal);
175 }
176};
177
178template <typename Enum>
179struct EnumFormatter : BufferFormatterBase<Enum> {
180 using BaseType = BufferFormatterBase<Enum>;
181 using EnumMap = EnumerationMap<Enum>;
182
183 using BaseType::BaseType;
184
185 template <typename Buffer>
186 void operator()(const UserTypes& types, Buffer& buffer) const {
187 auto literal = EnumMap::GetLiteral(this->value);
188 io::WriteBuffer(types, buffer, literal);
189 }
190};
191
192} // namespace detail
193
194namespace traits {
195
196template <typename T>
197struct Input<T,
199 std::is_enum<T>() && IsMappedToUserType<T>()>> {
200 using type = io::detail::EnumParser<T>;
201};
202
203template <typename T>
204struct ParserBufferCategory<io::detail::EnumParser<T>>
206
207template <typename T>
208struct Output<T,
210 std::is_enum<T>() && IsMappedToUserType<T>()>> {
211 using type = io::detail::EnumFormatter<T>;
212};
213
214} // namespace traits
215
216} // namespace storages::postgres::io
217
218USERVER_NAMESPACE_END