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) : 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/// An enumerators type that uses `Parse` and `ToString` functions for
59/// conversions.
60struct Codegen {};
61
62namespace detail {
63
64template <typename Enum, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
65struct AreEnumeratorsDefined : std::false_type {};
66
67template <typename Enum>
68struct AreEnumeratorsDefined<Enum, USERVER_NAMESPACE::utils::void_t<decltype(CppToUserPg<Enum>::enumerators)*>>
69 : std::true_type {};
70
71template <typename Enum>
72struct Enumerators {
73 static_assert(std::is_enum<Enum>(), "Type must be an enumeration");
74 static_assert(
75 AreEnumeratorsDefined<Enum>(),
76 "CppToUserPg for an enumeration must contain a static "
77 "`enumerators` member of `utils::TrivialBiMap` type or "
78 "`storages::postgres::io::detail::Enumerator[]`"
79 );
80 using Type = decltype(CppToUserPg<Enum>::enumerators);
81};
82
83template <typename Enum, typename = typename Enumerators<Enum>::Type>
84class EnumerationMap;
85
86template <typename Enum, typename T>
87class EnumerationMap {
88 using StringType = std::string_view;
89 using EnumType = Enum;
90 using MappingType = CppToUserPg<EnumType>;
91 using EnumeratorType = Enumerator<EnumType>;
92
93 struct EnumHash {
94 std::size_t operator()(EnumType v) const {
95 using underlying_type = std::underlying_type_t<EnumType>;
96 using hasher = std::hash<underlying_type>;
97 return hasher{}(static_cast<underlying_type>(v));
98 }
99 };
100
101 using EnumToString = std::unordered_map<EnumType, StringType, EnumHash>;
102 using StringToEnum = std::unordered_map<StringType, EnumType, utils::StringViewHash>;
103 using MapsPair = std::pair<EnumToString, StringToEnum>;
104
105 static MapsPair MapLiterals() {
106 MapsPair maps;
107 for (const auto& enumerator : enumerators) {
108 maps.first.insert(std::make_pair(enumerator.enumerator, enumerator.literal));
109 maps.second.insert(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
120public:
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{compiler::GetTypeName<EnumType>(), std::string{literal.data(), literal.size()}};
129 }
130 static StringType GetLiteral(EnumType enumerator) {
131 static const auto& map = EnumeratorMap();
132 if (auto f = map.find(enumerator); f != map.end()) {
133 return f->second;
134 }
135 throw InvalidEnumerationValue{enumerator};
136 }
137};
138
139template <typename Enum, typename Func>
140class EnumerationMap<Enum, const USERVER_NAMESPACE::utils::TrivialBiMap<Func>> {
141 using StringType = std::string_view;
142 using EnumType = Enum;
143 using MappingType = CppToUserPg<EnumType>;
144
145public:
146 static constexpr const auto& enumerators = MappingType::enumerators;
147 static constexpr std::size_t size = enumerators.size();
148 static constexpr EnumType GetEnumerator(StringType literal) {
149 auto enumerator = enumerators.TryFind(literal);
150 if (enumerator.has_value()) return *enumerator;
151 throw InvalidEnumerationLiteral{compiler::GetTypeName<EnumType>(), std::string{literal.data(), literal.size()}};
152 }
153 static constexpr StringType GetLiteral(EnumType enumerator) {
154 auto literal = enumerators.TryFind(enumerator);
155 if (literal.has_value()) return *literal;
156 throw InvalidEnumerationValue{enumerator};
157 }
158};
159
160template <typename Enum>
161class EnumerationMap<Enum, const Codegen> {
162 using EnumType = Enum;
163 using MappingType = CppToUserPg<EnumType>;
164
165public:
166 static EnumType GetEnumerator(std::string_view literal) { return Parse(literal, formats::parse::To<Enum>{}); }
167 static std::string GetLiteral(EnumType enumerator) { return ToString(enumerator); }
168};
169
170template <typename Enum>
171struct EnumParser : BufferParserBase<Enum> {
172 using BaseType = BufferParserBase<Enum>;
173 using EnumMap = EnumerationMap<Enum>;
174
175 using BaseType::BaseType;
176
177 void operator()(const FieldBuffer& buffer) {
178 std::string_view literal;
179 io::ReadBuffer(buffer, literal);
180 this->value = EnumMap::GetEnumerator(literal);
181 }
182};
183
184template <typename Enum>
185struct EnumFormatter : BufferFormatterBase<Enum> {
186 using BaseType = BufferFormatterBase<Enum>;
187 using EnumMap = EnumerationMap<Enum>;
188
189 using BaseType::BaseType;
190
191 template <typename Buffer>
192 void operator()(const UserTypes& types, Buffer& buffer) const {
193 auto literal = EnumMap::GetLiteral(this->value);
194 io::WriteBuffer(types, buffer, literal);
195 }
196};
197
198} // namespace detail
199
200namespace traits {
201
202template <typename T>
204 using type = io::detail::EnumParser<T>;
205};
206
207template <typename T>
208struct ParserBufferCategory<io::detail::EnumParser<T>>
210
211template <typename T>
212struct Output<
213 T,
215 using type = io::detail::EnumFormatter<T>;
216};
217
218} // namespace traits
219
220} // namespace storages::postgres::io
221
222USERVER_NAMESPACE_END