userver: userver/storages/postgres/io/enum_types.hpp Source File
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) : enumerator{en}, literal{lit} {}
32};
33
34} // namespace detail
35
36/// Optional base template for providing type aliases for defining enumeration
37/// mapping.
38template <typename Enum>
40 static_assert(std::is_enum<Enum>(), "Type must be an enumeration");
41
42 /// @brief Type alias for the enumeration.
43 ///
44 /// As the mapping must be specialized in `storages::postgres::io` namespace,
45 /// the enumerator value can be quite a long name. This type alias is a
46 /// shortcut to the enumeration type.
47 using EnumType = Enum;
48
49 /// Type alias for enumerator literal value holder
50 using Enumerator = detail::Enumerator<Enum>;
51
52 /// @brief Type alias for enumerator-literal mapping.
53 ///
54 /// See @ref pg_enum
55 using EnumeratorList = const std::initializer_list<Enumerator>;
56};
57
58/// Helper for enumerators that use `Parse` and `ToString` functions for
59/// conversions, mostly for autogenerated enums from chaotic.
60///
61/// A generated enum:
62/// @snippet storages/postgres/tests/enums_pgtest.cpp autogenerated enum type postgres
63/// can be registered in the following way
64/// @snippet storages/postgres/tests/enums_pgtest.cpp autogenerated enum registration postgres
65///
66/// After that the enum is mapped to the PostgreSQL type that can be created in the following way:
67/// @snippet storages/postgres/tests/enums_pgtest.cpp User enum type postgres
68/// Use it as usual:
69/// @snippet storages/postgres/tests/enums_pgtest.cpp autogenerated enum usage postgres
70struct Codegen {};
71
72namespace detail {
73
74template <typename Enum, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
75struct AreEnumeratorsDefined : std::false_type {};
76
77template <typename Enum>
78struct AreEnumeratorsDefined<Enum, USERVER_NAMESPACE::utils::void_t<decltype(CppToUserPg<Enum>::enumerators)*>>
79 : std::true_type {};
80
81template <typename Enum>
82struct Enumerators {
83 static_assert(std::is_enum<Enum>(), "Type must be an enumeration");
84 static_assert(
85 AreEnumeratorsDefined<Enum>(),
86 "CppToUserPg for an enumeration must contain a static "
87 "`enumerators` member of `utils::TrivialBiMap` type or "
88 "`storages::postgres::io::detail::Enumerator[]`"
89 );
90 using Type = decltype(CppToUserPg<Enum>::enumerators);
91};
92
93template <typename Enum, typename = typename Enumerators<Enum>::Type>
94class EnumerationMap;
95
96template <typename Enum, typename T>
97class EnumerationMap {
98 using StringType = std::string_view;
99 using EnumType = Enum;
100 using MappingType = CppToUserPg<EnumType>;
101 using EnumeratorType = Enumerator<EnumType>;
102
103 struct EnumHash {
104 std::size_t operator()(EnumType v) const {
105 using underlying_type = std::underlying_type_t<EnumType>;
106 using hasher = std::hash<underlying_type>;
107 return hasher{}(static_cast<underlying_type>(v));
108 }
109 };
110
111 using EnumToString = std::unordered_map<EnumType, StringType, EnumHash>;
112 using StringToEnum = std::unordered_map<StringType, EnumType, utils::StringViewHash>;
113 using MapsPair = std::pair<EnumToString, StringToEnum>;
114
115 static MapsPair MapLiterals() {
116 MapsPair maps;
117 for (const auto& enumerator : enumerators) {
118 maps.first.insert(std::make_pair(enumerator.enumerator, enumerator.literal));
119 maps.second.insert(std::make_pair(enumerator.literal, enumerator.enumerator));
120 }
121 return maps;
122 }
123 static const MapsPair& GetMaps() {
124 static auto maps_ = MapLiterals();
125 return maps_;
126 }
127 static const EnumToString& EnumeratorMap() { return GetMaps().first; }
128 static const StringToEnum& LiteralMap() { return GetMaps().second; }
129
130public:
131 static constexpr const auto& enumerators = MappingType::enumerators;
132 static constexpr std::size_t size = std::size(enumerators);
133 static EnumType GetEnumerator(StringType literal) {
134 static const auto& map = LiteralMap();
135 if (auto f = map.find(literal); f != map.end()) {
136 return f->second;
137 }
138 throw InvalidEnumerationLiteral{compiler::GetTypeName<EnumType>(), std::string{literal.data(), literal.size()}};
139 }
140 static StringType GetLiteral(EnumType enumerator) {
141 static const auto& map = EnumeratorMap();
142 if (auto f = map.find(enumerator); f != map.end()) {
143 return f->second;
144 }
145 throw InvalidEnumerationValue{enumerator};
146 }
147};
148
149template <typename Enum, typename Func>
150class EnumerationMap<Enum, const USERVER_NAMESPACE::utils::TrivialBiMap<Func>> {
151 using StringType = std::string_view;
152 using EnumType = Enum;
153 using MappingType = CppToUserPg<EnumType>;
154
155public:
156 static constexpr const auto& enumerators = MappingType::enumerators;
157 static constexpr std::size_t size = enumerators.size();
158 static constexpr EnumType GetEnumerator(StringType literal) {
159 auto enumerator = enumerators.TryFind(literal);
160 if (enumerator.has_value()) return *enumerator;
161 throw InvalidEnumerationLiteral{compiler::GetTypeName<EnumType>(), std::string{literal.data(), literal.size()}};
162 }
163 static constexpr StringType GetLiteral(EnumType enumerator) {
164 auto literal = enumerators.TryFind(enumerator);
165 if (literal.has_value()) return *literal;
166 throw InvalidEnumerationValue{enumerator};
167 }
168};
169
170template <typename Enum>
171class EnumerationMap<Enum, const Codegen> {
172 using EnumType = Enum;
173 using MappingType = CppToUserPg<EnumType>;
174
175public:
176 static EnumType GetEnumerator(std::string_view literal) { return Parse(literal, formats::parse::To<Enum>{}); }
177 static std::string GetLiteral(EnumType enumerator) { return ToString(enumerator); }
178};
179
180template <typename Enum>
181struct EnumParser : BufferParserBase<Enum> {
182 using BaseType = BufferParserBase<Enum>;
183 using EnumMap = EnumerationMap<Enum>;
184
185 using BaseType::BaseType;
186
187 void operator()(const FieldBuffer& buffer) {
188 std::string_view literal;
189 io::ReadBuffer(buffer, literal);
190 this->value = EnumMap::GetEnumerator(literal);
191 }
192};
193
194template <typename Enum>
195struct EnumFormatter : BufferFormatterBase<Enum> {
196 using BaseType = BufferFormatterBase<Enum>;
197 using EnumMap = EnumerationMap<Enum>;
198
199 using BaseType::BaseType;
200
201 template <typename Buffer>
202 void operator()(const UserTypes& types, Buffer& buffer) const {
203 auto literal = EnumMap::GetLiteral(this->value);
204 io::WriteBuffer(types, buffer, literal);
205 }
206};
207
208} // namespace detail
209
210namespace traits {
211
212template <typename T>
213struct Input<T, std::enable_if_t<!detail::kCustomParserDefined<T> && std::is_enum<T>() && IsMappedToUserType<T>()>> {
214 using type = io::detail::EnumParser<T>;
215};
216
217template <typename T>
218struct ParserBufferCategory<io::detail::EnumParser<T>>
219 : std::integral_constant<BufferCategory, BufferCategory::kPlainBuffer> {};
220
221template <typename T>
222struct Output<
223 T,
224 std::enable_if_t<!detail::kCustomFormatterDefined<T> && std::is_enum<T>() && IsMappedToUserType<T>()>> {
225 using type = io::detail::EnumFormatter<T>;
226};
227
228} // namespace traits
229
230} // namespace storages::postgres::io
231
232USERVER_NAMESPACE_END