Skip to content

Commit b49d32c

Browse files
committed
Fixes
1 parent 82003c1 commit b49d32c

File tree

4 files changed

+237
-102
lines changed

4 files changed

+237
-102
lines changed

universal/include/userver/formats/universal/common_containers.hpp

Lines changed: 118 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#if __cplusplus >= 202002L
12
#pragma once
23
#include <userver/formats/universal/universal.hpp>
34
#include <userver/formats/common/items.hpp>
@@ -11,31 +12,35 @@ template <typename T>
1112
struct Max : public impl::Param<T> {
1213
inline constexpr Max(const T& value) :
1314
impl::Param<T>(value) {}
14-
inline constexpr std::string Check(const T& value) const {
15-
return value <= this->value ? "" : this->Error(value);
15+
inline constexpr std::optional<std::string> Check(const T& value) const {
16+
if(value <= this->value) {
17+
return std::nullopt;
18+
}
19+
return this->Error(value);
1620
}
1721
template <typename Value>
18-
inline constexpr std::enable_if_t<meta::kIsRange<Value>, std::string>
22+
inline constexpr std::enable_if_t<meta::kIsRange<Value>, std::optional<std::string>>
1923
Check(const Value& value) const {
2024
for(const auto& element : value) {
2125
if(element > this->value) {
2226
return this->Error(element);
2327
}
2428
}
25-
return "";
29+
return std::nullopt;
2630
}
2731
inline constexpr std::string Error(const T& value) const {
2832
return std::format("{} > {}", value, this->value);
2933
}
3034
};
3135

36+
3237
struct MinElements : public impl::Param<std::size_t> {
3338
inline constexpr MinElements(const std::size_t& value) :
3439
impl::Param<std::size_t>(value) {}
3540
template <typename Value>
36-
inline constexpr std::string Check(const Value& value) const {
41+
inline constexpr std::optional<std::string> Check(const Value& value) const {
3742
if(value.size() >= this->value) {
38-
return "";
43+
return std::nullopt;
3944
}
4045
return this->Error(value);
4146
}
@@ -49,18 +54,21 @@ template <typename T>
4954
struct Min : public impl::Param<T> {
5055
inline constexpr Min(const T& value) :
5156
impl::Param<T>(value) {}
52-
inline constexpr std::string Check(const T& value) const {
53-
return value >= this->value ? "" : this->Error(value);
57+
inline constexpr std::optional<std::string> Check(const T& value) const {
58+
if(value >= this->value) {
59+
return std::nullopt;
60+
}
61+
return this->Error(value);
5462
};
5563
template <typename Value>
56-
inline constexpr std::enable_if_t<meta::kIsRange<Value>, std::string>
64+
inline constexpr std::enable_if_t<meta::kIsRange<Value>, std::optional<std::string>>
5765
Check(const Value& value) const {
5866
for(const auto& element : value) {
5967
if(element < this->value) {
6068
return this->Error(element);
6169
}
6270
}
63-
return "";
71+
return std::nullopt;
6472
}
6573
inline constexpr auto Error(const T& value) const {
6674
return std::format("{} < {}", value, this->value);
@@ -77,8 +85,11 @@ static const utils::regex kRegex(Pattern);
7785
struct Pattern : public impl::EmptyCheck, public impl::Param<const utils::regex*> {
7886
constexpr inline Pattern(const utils::regex& regex) :
7987
impl::Param<const utils::regex*>(&regex) {}
80-
constexpr inline std::string Check(std::string_view str) const {
81-
return utils::regex_match(str, *this->value) ? "" : this->Error(str);
88+
constexpr inline std::optional<std::string> Check(std::string_view str) const {
89+
if(utils::regex_match(str, *this->value)) {
90+
return std::nullopt;
91+
}
92+
return this->Error(str);
8293
}
8394
constexpr inline std::string Error(std::string_view) const {
8495
return "Error";
@@ -90,8 +101,8 @@ struct Additional : public impl::EmptyCheck, public impl::Param<bool> {
90101
};
91102
template <>
92103
struct FieldConfig<int> {
93-
std::optional<Max<int>> Maximum;
94-
std::optional<Min<int>> Minimum;
104+
std::optional<Max<int>> Maximum = std::nullopt;
105+
std::optional<Min<int>> Minimum = std::nullopt;
95106
template <typename MainClass, auto I, typename Value>
96107
constexpr int Read(Value&& value) const {
97108
constexpr auto name = boost::pfr::get_name<I, MainClass>();
@@ -105,48 +116,74 @@ struct FieldConfig<int> {
105116
constexpr auto Write(const int& value, std::string_view fieldName, const auto&, auto& builder) const {
106117
builder[static_cast<std::string>(fieldName)] = value;
107118
};
108-
inline constexpr std::string_view Check(const int&) const {
109-
return "";
119+
inline constexpr std::optional<std::string> Check(const int&) const {
120+
return std::nullopt;
110121
}
111122

112123
};
113124
template <>
114125
struct FieldConfig<std::optional<std::string>> {
115-
std::optional<Pattern> Pattern;
116-
std::optional<Default<std::string>> Default;
126+
std::optional<Pattern> Pattern = std::nullopt;
127+
std::optional<Default<std::string_view>> Default = std::nullopt;
128+
bool Required = false;
129+
bool Nullable = false;
117130
template <typename MainClass, auto I, typename Value>
118131
constexpr std::optional<std::string> Read(Value&& value) const {
119132
constexpr auto name = boost::pfr::get_name<I, MainClass>();
120-
if(!value.HasMember(name)) {
133+
if(value[name].IsMissing() && this->Required) {
121134
return std::nullopt;
122135
};
123-
return value[name].template As<std::string>();
136+
if(!value[name].IsMissing()) {
137+
if(this->Nullable) {
138+
return value[name].template As<std::optional<std::string>>();
139+
};
140+
return value[name].template As<std::string>();
141+
};
142+
if(this->Default) {
143+
return static_cast<std::string>(this->Default->value);
144+
};
145+
return std::nullopt;
124146
};
125147
template <typename MainClass, auto I, typename Value>
126148
constexpr std::optional<std::optional<std::string>> TryRead(Value&& value) const {
127149
constexpr auto name = boost::pfr::get_name<I, MainClass>();
128-
auto response = parse::TryParse(value[name], parse::To<std::string>{});
129-
if(response) {
130-
return response;
131-
}
150+
if(value[name].IsMissing() && this->Required) {
151+
return std::nullopt;
152+
};
153+
154+
if(this->Nullable) {
155+
auto response = parse::TryParse(value[name], parse::To<std::optional<std::string>>{});
156+
if(response) {
157+
return response;
158+
}
159+
} else {
160+
auto response = parse::TryParse(value[name], parse::To<std::string>{});
161+
if(response) {
162+
return response;
163+
};
164+
};
132165
if(this->Default) {
133-
return this->Default->value;
166+
return static_cast<std::string>(this->Default->value);
134167
}
135-
return std::nullopt;
168+
return {{}};
136169
}
137170
constexpr auto Write(const std::optional<std::string>& value, std::string_view fieldName, const auto&, auto& builder) const {
138171
if(value) {
139172
builder[static_cast<std::string>(fieldName)] = *value;
173+
return;
174+
};
175+
if(this->Default) {
176+
builder[static_cast<std::string>(fieldName)] = this->Default->value;
140177
};
141178
};
142-
inline constexpr std::string_view Check(const std::string&) const {
143-
return "";
179+
inline constexpr std::optional<std::string> Check(const auto&) const {
180+
return std::nullopt;
144181
}
145182

146183
};
147184
template <>
148185
struct FieldConfig<std::string> {
149-
std::optional<Pattern> Pattern;
186+
std::optional<Pattern> Pattern = std::nullopt;
150187
template <typename MainClass, auto I, typename Value>
151188
constexpr std::string Read(Value&& value) const {
152189
constexpr auto name = boost::pfr::get_name<I, MainClass>();
@@ -160,21 +197,29 @@ struct FieldConfig<std::string> {
160197
constexpr auto Write(std::string_view value, std::string_view fieldName, const auto&, auto& builder) const {
161198
builder[static_cast<std::string>(fieldName)] = value;
162199
};
163-
inline constexpr std::string_view Check(std::string_view) const {
164-
return "";
200+
inline constexpr std::optional<std::string> Check(std::string_view) const {
201+
return std::nullopt;
165202
}
166203

167204
};
168205

169206
template <>
170207
struct FieldConfig<std::optional<int>> {
171-
std::optional<Max<int>> Maximum;
172-
std::optional<Min<int>> Minimum;
173-
std::optional<Default<int>> Default;
208+
std::optional<Max<int>> Maximum = std::nullopt;
209+
std::optional<Min<int>> Minimum = std::nullopt;
210+
std::optional<Default<int>> Default = std::nullopt;
211+
bool Required = false;
212+
bool Nullable = false;
174213
template <typename MainClass, auto I, typename Value>
175214
constexpr std::optional<int> Read(Value&& value) const {
176215
constexpr auto name = boost::pfr::get_name<I, MainClass>();
177-
if(value.HasMember(name)) {
216+
if(value[name].IsMissing() && this->Required) {
217+
return std::nullopt;
218+
};
219+
if(!value[name].IsMissing()) {
220+
if(this->Nullable) {
221+
return value[name].template As<std::optional<int>>();
222+
};
178223
return value[name].template As<int>();
179224
}
180225
if(this->Default) {
@@ -185,10 +230,20 @@ struct FieldConfig<std::optional<int>> {
185230
template <typename MainClass, auto I, typename Value>
186231
constexpr std::optional<std::optional<int>> TryRead(Value&& value) const {
187232
constexpr auto name = boost::pfr::get_name<I, MainClass>();
188-
auto response = parse::TryParse(value[name], parse::To<int>{});
189-
if(response) {
190-
return response;
191-
}
233+
if(this->Nullable) {
234+
auto response = parse::TryParse(value[name], parse::To<std::optional<int>>{});
235+
if(response) {
236+
return response;
237+
}
238+
} else {
239+
if(value[name].IsNull()) {
240+
return std::nullopt;
241+
};
242+
auto response = parse::TryParse(value[name], parse::To<int>{});
243+
if(response) {
244+
return response;
245+
};
246+
};
192247
if(this->Default) {
193248
return this->Default->value;
194249
}
@@ -204,21 +259,21 @@ struct FieldConfig<std::optional<int>> {
204259
}
205260
}
206261

207-
inline constexpr std::string_view Check(const std::optional<int>&) const {
208-
return "";
262+
inline constexpr std::optional<std::string> Check(const std::optional<int>&) const {
263+
return std::nullopt;
209264
}
210265

211266
};
212267
template <typename Value>
213268
struct FieldConfig<std::unordered_map<std::string, Value>> {
214-
std::optional<Additional> Additional;
215-
using kType = std::unordered_map<std::string, Value>;
269+
std::optional<Additional> Additional = std::nullopt;
270+
using Type = std::unordered_map<std::string, Value>;
216271
template <typename MainClass, auto I, typename Value2>
217-
inline constexpr kType Read(Value2&& value) const {
272+
inline constexpr Type Read(Value2&& value) const {
218273
if(!this->Additional) {
219274
throw std::runtime_error("Invalid Flags");
220275
}
221-
kType response;
276+
Type response;
222277
constexpr auto fields = boost::pfr::names_as_array<MainClass>();
223278
for(const auto& [name, value2] : userver::formats::common::Items(std::forward<Value2>(value))) {
224279
auto it = std::find(fields.begin(), fields.end(), name);
@@ -229,11 +284,11 @@ struct FieldConfig<std::unordered_map<std::string, Value>> {
229284
return response;
230285
}
231286
template <typename MainClass, auto I, typename Value2>
232-
inline constexpr std::optional<kType> TryRead(Value2&& value) const {
287+
inline constexpr std::optional<Type> TryRead(Value2&& value) const {
233288
if(!this->Additional) {
234289
throw std::runtime_error("Invalid Flags");
235290
}
236-
kType response;
291+
Type response;
237292
constexpr auto fields = boost::pfr::names_as_array<MainClass>();
238293
constexpr auto name = boost::pfr::get_name<I, MainClass>();
239294
for(const auto& [name2, value2] : userver::formats::common::Items(std::forward<Value2>(value))) {
@@ -247,19 +302,19 @@ struct FieldConfig<std::unordered_map<std::string, Value>> {
247302
}
248303
return response;
249304
}
250-
inline constexpr std::string_view Check(const kType&) const {
251-
return "";
305+
inline constexpr std::optional<std::string> Check(const Type&) const {
306+
return std::nullopt;
252307
}
253-
constexpr auto Write(const kType& value, std::string_view, const auto&, auto& builder) const {
308+
constexpr auto Write(const Type& value, std::string_view, const auto&, auto& builder) const {
254309
for(const auto& [name, value2] : value) {
255310
builder[name] = value2;
256311
};
257312
};
258313
};
259314
template <typename Element>
260315
struct FieldConfig<std::vector<Element>> {
261-
std::optional<MinElements> MinimalElements;
262-
FieldConfig<Element> Items;
316+
std::optional<MinElements> MinimalElements = std::nullopt;
317+
FieldConfig<Element> Items = {};
263318
template <typename MainClass, auto I, typename Value>
264319
inline constexpr auto Read(Value&& value) const {
265320
constexpr auto name = boost::pfr::get_name<I, MainClass>();
@@ -283,14 +338,22 @@ struct FieldConfig<std::vector<Element>> {
283338
}
284339
return response;
285340
}
286-
inline constexpr std::string Check(const std::vector<Element>& obj) const {
287-
std::string error;
341+
inline constexpr std::optional<std::string> Check(const std::vector<Element>& obj) const {
342+
std::optional<std::string> error = std::nullopt;
288343
for(const auto& element : obj) {
289-
error += impl::UniversalCheckField(element, this->Items);
344+
auto add = impl::UniversalCheckField(element, this->Items);
345+
if(add) {
346+
if(!error) {
347+
error = *add;
348+
continue;
349+
};
350+
*error += *add;
351+
}
290352
}
291353
return error;
292354
}
293355

294356
};
295357
} // namespace formats::universal
296358
USERVER_NAMESPACE_END
359+
#endif

0 commit comments

Comments
 (0)