userver: userver/storages/postgres/io/strong_typedef.hpp Source File
Loading...
Searching...
No Matches
strong_typedef.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/io/strong_typedef.hpp
4/// @brief utils::StrongTypedef I/O support
5/// @ingroup userver_postgres_parse_and_format
6
7#include <userver/storages/postgres/exceptions.hpp>
8#include <userver/storages/postgres/io/buffer_io.hpp>
9#include <userver/storages/postgres/io/buffer_io_base.hpp>
10#include <userver/storages/postgres/io/nullable_traits.hpp>
11
12#include <userver/utils/strong_typedef.hpp>
13
14USERVER_NAMESPACE_BEGIN
15
16namespace storages::postgres::io {
17
18namespace traits {
19
20namespace impl {
21
22template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
23concept IsStrongTypedefDirectlyMapped =
24 IsMappedToUserType<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>> ||
25 IsMappedToSystemType<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>> ||
26 IsMappedToArray<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>>;
27
28} // namespace impl
29
30template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
31struct IsMappedToPg<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>>
32 // NOLINTNEXTLINE(google-readability-casting)
33 : BoolConstant<impl::IsStrongTypedefDirectlyMapped<Tag, T, Ops> || kIsMappedToPg<T>> {};
34
35// Mark that strong typedef mapping is a special case for disambiguating
36// specialization of CppToPg
37template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
38struct IsSpecialMapping<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>>
39 // NOLINTNEXTLINE(google-readability-casting)
40 : BoolConstant<!impl::IsStrongTypedefDirectlyMapped<Tag, T, Ops> && kIsMappedToPg<T>> {};
41
42template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
43struct IsNullable<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>> : IsNullable<T> {};
44
45template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
46struct GetSetNull<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>> {
47 using ValueType = USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>;
48 using UnderlyingGetSet = GetSetNull<T>;
49 inline static bool IsNull(const ValueType& v) { return UnderlyingGetSet::IsNull(v.GetUnderlying()); }
50 inline static void SetNull(ValueType& v) { UnderlyingGetSet::SetNull(v.GetUnderlying()); }
51 inline static void SetDefault(ValueType& v) { UnderlyingGetSet::SetDefault(v.GetUnderlying()); }
52};
53
54/// A metafunction that enables an enum type serialization to its
55/// underlying type. Can be specialized.
56template <typename T>
57struct CanUseEnumAsStrongTypedef : std::false_type {};
58
59namespace impl {
60
61template <typename T>
62constexpr bool CheckCanUseEnumAsStrongTypedef() {
63 if constexpr (CanUseEnumAsStrongTypedef<T>{}) {
64 static_assert(
65 std::is_enum_v<T>,
66 "storages::postgres::io::traits::CanUseEnumAsStrongTypedef "
67 "should be specialized only for enums"
68 );
69 static_assert(
70 std::is_signed_v<std::underlying_type_t<T>>,
71 "storages::postgres::io::traits::CanUseEnumAsStrongTypedef should be "
72 "specialized only for enums with signed underlying type"
73 );
74
75 return true;
76 }
77
78 return false;
79}
80
81} // namespace impl
82
83template <typename T>
84concept RequiresCanUseEnumAsStrongTypedef = impl::CheckCanUseEnumAsStrongTypedef<T>();
85
86} // namespace traits
87
88template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
89struct BufferFormatter<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>>
90 : detail::BufferFormatterBase<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>> {
91 using BaseType = detail::BufferFormatterBase<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>>;
92 using BaseType::BaseType;
93
94 template <typename Buffer>
95 void operator()(const UserTypes& types, Buffer& buf) const {
96 io::WriteBuffer(types, buf, this->value.GetUnderlying());
97 }
98};
99
100namespace detail {
101template <typename StrongTypedef, bool Categories = false>
102struct StrongTypedefParser : BufferParserBase<StrongTypedef> {
103 using BaseType = BufferParserBase<StrongTypedef>;
104 using UnderlyingType = typename StrongTypedef::UnderlyingType;
105
106 using BaseType::BaseType;
107
108 void operator()(const FieldBuffer& buffer) {
109 UnderlyingType& v = this->value.GetUnderlying();
110 io::ReadBuffer(buffer, v);
111 }
112};
113
114template <typename StrongTypedef>
115struct StrongTypedefParser<StrongTypedef, true> : BufferParserBase<StrongTypedef> {
116 using BaseType = BufferParserBase<StrongTypedef>;
117 using UnderlyingType = typename StrongTypedef::UnderlyingType;
118
119 using BaseType::BaseType;
120
121 void operator()(const FieldBuffer& buffer, const TypeBufferCategory& categories) {
122 UnderlyingType& v = this->value.GetUnderlying();
123 io::ReadBuffer(buffer, v, categories);
124 }
125};
126
127} // namespace detail
128
129template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
130struct BufferParser<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>>
131 : detail::StrongTypedefParser<
132 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>,
133 detail::kParserRequiresTypeCategories<T>> {
134 using BaseType = detail::StrongTypedefParser<
135 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>,
136 detail::kParserRequiresTypeCategories<T>>;
137 using BaseType::BaseType;
138};
139
140// StrongTypedef template mapping specialization
141template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
142requires(!traits::impl::IsStrongTypedefDirectlyMapped<Tag, T, Ops> && traits::kIsMappedToPg<T>)
143struct CppToPg<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>> : CppToPg<T> {};
144
145namespace traits {
146
147template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops>
148struct ParserBufferCategory<BufferParser<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops>>>
149 : ParserBufferCategory<typename traits::IO<T>::ParserType> {};
150
151} // namespace traits
152
153namespace detail {
154
155template <typename T>
156struct EnumStrongTypedefFormatter : BufferFormatterBase<T> {
157 using BaseType = BufferFormatterBase<T>;
158 using BaseType::BaseType;
159
160 template <typename Buffer>
161 void operator()(const UserTypes& types, Buffer& buf) const {
162 io::WriteBuffer(types, buf, USERVER_NAMESPACE::utils::UnderlyingValue(this->value));
163 }
164};
165
166template <typename T>
167struct EnumStrongTypedefParser : BufferParserBase<T> {
168 using BaseType = BufferParserBase<T>;
169 using ValueType = typename BaseType::ValueType;
170 using UnderlyingType = std::underlying_type_t<ValueType>;
171
172 using BaseType::BaseType;
173
174 void operator()(const FieldBuffer& buffer) {
175 UnderlyingType v;
176 io::ReadBuffer(buffer, v);
177 this->value = static_cast<ValueType>(v);
178 }
179};
180
181} // namespace detail
182
183namespace traits {
184
186struct Output<T> {
187 using type = io::detail::EnumStrongTypedefFormatter<T>;
188};
189
191struct Input<T> {
192 using type = io::detail::EnumStrongTypedefParser<T>;
193};
194
196struct IsMappedToPg<T> : std::true_type {};
197
199struct IsSpecialMapping<T> : std::true_type {};
200
201} // namespace traits
202
203// enum class strong typedef mapping specialization
205struct CppToPg<T> : CppToPg<std::underlying_type_t<T>> {};
206
207} // namespace storages::postgres::io
208
209USERVER_NAMESPACE_END