userver: userver/storages/postgres/io/enum_types.hpp Source File
Loading...
Searching...
No Matches
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