Skip to content

Commit 419345b

Browse files
committed
feat chaotic: add TryConvert helper function for enums and use Convert consistently
Close to the protobuf's `bool Enum_IsValid()` and `bool Enum_Parse()` functions Right now there is no way to perform enum validity check having string value without throwing an exception. Tests: протестировано CI Closes: #1157 commit_hash:95cbf5a663732b08586577a9497c0d34e6f4e395
1 parent c0a301e commit 419345b

18 files changed

Lines changed: 162 additions & 50 deletions

File tree

chaotic/chaotic/back/cpp/templates/type.cpp.jinja

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,20 @@
6060
{% if type.need_dom_parser() %}
6161
{% set local_name = shortest_cpp_name(type) %}
6262
{% if type.get_py_type() == 'CppIntEnum' %}
63-
{{ local_name }} FromInt(std::int64_t value, {{ userver }}::formats::parse::To<{{ local_name }}>) {
63+
{{ local_name }} Convert(std::int64_t value, {{ userver }}::chaotic::convert::To<{{ local_name }}>) {
6464
const auto result = k{{ type.cpp_global_struct_field_name() }}_Mapping.TryFindBySecond(value);
6565
if (result.has_value()) {
6666
return *result;
6767
}
6868
throw std::runtime_error(fmt::format("Invalid enum value ({}) for type {{ type.cpp_global_name() }}", value));
6969
}
70+
71+
std::optional<{{ local_name }}> TryConvert(std::int64_t value, {{ userver }}::chaotic::convert::To<{{ local_name }}>) noexcept {
72+
return k{{ type.cpp_global_struct_field_name() }}_Mapping.TryFindBySecond(value);
73+
}
7074
{% elif type.get_py_type() == 'CppStringEnum' %}
71-
{{ local_name }} FromString(std::string_view value,
72-
{{userver}}::formats::parse::To<{{ local_name }}>)
75+
{{ local_name }} Convert(std::string_view value,
76+
{{userver}}::chaotic::convert::To<{{ local_name }}>)
7377
{
7478
const auto result = k{{ type.cpp_global_struct_field_name() }}_Mapping.TryFindBySecond(value);
7579
if (result.has_value()) {
@@ -78,10 +82,14 @@
7882
throw std::runtime_error(fmt::format("Invalid enum value ({}) for type {{ type.cpp_global_name() }}", value));
7983
}
8084

81-
{{ local_name }} Parse(std::string_view value,
82-
{{userver}}::formats::parse::To<{{ local_name }}> to)
85+
std::optional<{{ local_name }}> TryConvert(std::string_view value, {{userver}}::chaotic::convert::To<{{ local_name }}>) noexcept
86+
{
87+
return k{{ type.cpp_global_struct_field_name() }}_Mapping.TryFindBySecond(value);
88+
}
89+
90+
{{ local_name }} Parse(std::string_view value, {{userver}}::formats::parse::To<{{ local_name }}>)
8391
{
84-
return FromString(value, to);
92+
return Convert(value, {{userver}}::chaotic::convert::To<{{ local_name }}>{});
8593
}
8694
{% endif %}
8795
{% endif %}

chaotic/chaotic/back/cpp/templates/type.hpp.jinja

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,16 @@
242242

243243
{% if type.need_dom_parser() %}
244244
{% if type.get_py_type() == 'CppIntEnum' %}
245-
{{ shortest_cpp_name(type) }} FromInt(std::int64_t value, {{ userver }}::formats::parse::To<{{ shortest_cpp_name(type) }}>);
245+
{{ shortest_cpp_name(type) }} Convert(std::int64_t value, {{ userver }}::chaotic::convert::To<{{ shortest_cpp_name(type) }}>);
246+
247+
std::optional<{{ shortest_cpp_name(type) }}> TryConvert(std::int64_t value,
248+
{{ userver }}::chaotic::convert::To<{{ shortest_cpp_name(type) }}>) noexcept;
246249
{% elif type.get_py_type() == 'CppStringEnum' %}
247-
{{ type.raw_cpp_type.in_scope(get_current_namespace()) }} FromString(std::string_view value,
248-
{{ userver }}::formats::parse::To<{{ shortest_cpp_name(type) }}>);
250+
{{ type.raw_cpp_type.in_scope(get_current_namespace()) }} Convert(std::string_view value,
251+
{{ userver }}::chaotic::convert::To<{{ shortest_cpp_name(type) }}>);
252+
253+
std::optional<{{ shortest_cpp_name(type) }}> TryConvert(std::string_view value,
254+
{{ userver }}::chaotic::convert::To<{{ shortest_cpp_name(type) }}>) noexcept;
249255

250256
{# backward compatibility #}
251257
{{shortest_cpp_name(type)}} Parse(std::string_view value,

chaotic/chaotic/back/cpp/templates/type_parsers.ipp.jinja

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,14 @@
104104
{% elif type.get_py_type() == 'CppIntEnum' %}
105105
template<typename Value, typename = std::enable_if_t<{{ userver }}::formats::common::kIsFormatValue<Value>>>
106106
{{ shortest_cpp_name(type, blacklist=['Value']) }} Parse(Value val,
107-
{{ userver }}::formats::parse::To<{{ shortest_cpp_name(type, blacklist=['Value']) }}> to)
107+
{{ userver }}::formats::parse::To<{{ shortest_cpp_name(type, blacklist=['Value']) }}>)
108108
{
109-
const auto value = val.template As<std::int32_t>();
110-
try {
111-
return FromInt(value, to);
112-
} catch (const std::exception&) {
109+
const auto value = val.template As<std::int64_t>();
110+
const auto converted_opt = TryConvert(value, {{ userver }}::chaotic::convert::To<{{ shortest_cpp_name(type) }}>{});
111+
if (!converted_opt) {
113112
{{ userver }}::chaotic::ThrowForValue(fmt::format("Invalid enum value ({}) for type {{ type.cpp_global_name() }}", value), val);
114113
}
114+
return *converted_opt;
115115
}
116116
{% elif type.get_py_type() == 'CppStringEnum' %}
117117
template<typename Value, typename = std::enable_if_t<{{ userver }}::formats::common::kIsFormatValue<Value>>>

chaotic/chaotic/back/cpp/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ def get_default(self) -> str:
630630
elif isinstance(default, int):
631631
default = f'{default}LL'
632632
type_ = self.schema.user_cpp_type
633-
return f'Convert({default}, USERVER_NAMESPACE::chaotic::convert::To<{type_}>{{}})'
633+
return f'USERVER_NAMESPACE::chaotic::ConvertTo<{type_}>({default})'
634634

635635
return f'{default}'
636636

chaotic/golden_tests/output/schemas/enum/enum.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,21 @@ Enum Parse(USERVER_NAMESPACE::yaml_config::Value json, USERVER_NAMESPACE::format
4545
return Parse<USERVER_NAMESPACE::yaml_config::Value>(json, to);
4646
}
4747

48-
Enum::Foo FromString(std::string_view value, USERVER_NAMESPACE::formats::parse::To<Enum::Foo>) {
48+
Enum::Foo Convert(std::string_view value, USERVER_NAMESPACE::chaotic::convert::To<Enum::Foo>) {
4949
const auto result = k__ns__Enum__Foo_Mapping.TryFindBySecond(value);
5050
if (result.has_value()) {
5151
return *result;
5252
}
5353
throw std::runtime_error(fmt::format("Invalid enum value ({}) for type ::ns::Enum::Foo", value));
5454
}
5555

56-
Enum::Foo Parse(std::string_view value, USERVER_NAMESPACE::formats::parse::To<Enum::Foo> to) {
57-
return FromString(value, to);
56+
std::optional<Enum::Foo> TryConvert(std::string_view value,
57+
USERVER_NAMESPACE::chaotic::convert::To<Enum::Foo>) noexcept {
58+
return k__ns__Enum__Foo_Mapping.TryFindBySecond(value);
59+
}
60+
61+
Enum::Foo Parse(std::string_view value, USERVER_NAMESPACE::formats::parse::To<Enum::Foo>) {
62+
return Convert(value, USERVER_NAMESPACE::chaotic::convert::To<Enum::Foo>{});
5863
}
5964

6065
USERVER_NAMESPACE::formats::json::Value Serialize(

chaotic/golden_tests/output/schemas/enum/enum.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ Enum Parse(USERVER_NAMESPACE::yaml_config::Value json, USERVER_NAMESPACE::format
4848

4949
Enum FromJsonString(std::string_view json, USERVER_NAMESPACE::formats::parse::To<Enum>);
5050

51-
Enum::Foo FromString(std::string_view value, USERVER_NAMESPACE::formats::parse::To<Enum::Foo>);
51+
Enum::Foo Convert(std::string_view value, USERVER_NAMESPACE::chaotic::convert::To<Enum::Foo>);
52+
53+
std::optional<Enum::Foo> TryConvert(std::string_view value,
54+
USERVER_NAMESPACE::chaotic::convert::To<Enum::Foo>) noexcept;
5255

5356
Enum::Foo Parse(std::string_view value, USERVER_NAMESPACE::formats::parse::To<Enum::Foo>);
5457

chaotic/golden_tests/output/schemas/production/min_messenger.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -713,8 +713,8 @@ V1LikeTriggerRequest Parse(USERVER_NAMESPACE::yaml_config::Value json,
713713
return Parse<USERVER_NAMESPACE::yaml_config::Value>(json, to);
714714
}
715715

716-
V1LikeTriggerRequest::Animation FromString(std::string_view value,
717-
USERVER_NAMESPACE::formats::parse::To<V1LikeTriggerRequest::Animation>) {
716+
V1LikeTriggerRequest::Animation Convert(std::string_view value,
717+
USERVER_NAMESPACE::chaotic::convert::To<V1LikeTriggerRequest::Animation>) {
718718
const auto result = k__ns__V1LikeTriggerRequest__Animation_Mapping.TryFindBySecond(value);
719719
if (result.has_value()) {
720720
return *result;
@@ -723,9 +723,14 @@ V1LikeTriggerRequest::Animation FromString(std::string_view value,
723723
fmt::format("Invalid enum value ({}) for type ::ns::V1LikeTriggerRequest::Animation", value));
724724
}
725725

726+
std::optional<V1LikeTriggerRequest::Animation> TryConvert(
727+
std::string_view value, USERVER_NAMESPACE::chaotic::convert::To<V1LikeTriggerRequest::Animation>) noexcept {
728+
return k__ns__V1LikeTriggerRequest__Animation_Mapping.TryFindBySecond(value);
729+
}
730+
726731
V1LikeTriggerRequest::Animation Parse(std::string_view value,
727-
USERVER_NAMESPACE::formats::parse::To<V1LikeTriggerRequest::Animation> to) {
728-
return FromString(value, to);
732+
USERVER_NAMESPACE::formats::parse::To<V1LikeTriggerRequest::Animation>) {
733+
return Convert(value, USERVER_NAMESPACE::chaotic::convert::To<V1LikeTriggerRequest::Animation>{});
729734
}
730735

731736
USERVER_NAMESPACE::formats::json::Value Serialize(

chaotic/golden_tests/output/schemas/production/min_messenger.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,8 +491,11 @@ V1LikeTriggerRequest Parse(USERVER_NAMESPACE::yaml_config::Value json,
491491

492492
V1LikeTriggerRequest FromJsonString(std::string_view json, USERVER_NAMESPACE::formats::parse::To<V1LikeTriggerRequest>);
493493

494-
V1LikeTriggerRequest::Animation FromString(std::string_view value,
495-
USERVER_NAMESPACE::formats::parse::To<V1LikeTriggerRequest::Animation>);
494+
V1LikeTriggerRequest::Animation Convert(std::string_view value,
495+
USERVER_NAMESPACE::chaotic::convert::To<V1LikeTriggerRequest::Animation>);
496+
497+
std::optional<V1LikeTriggerRequest::Animation> TryConvert(
498+
std::string_view value, USERVER_NAMESPACE::chaotic::convert::To<V1LikeTriggerRequest::Animation>) noexcept;
496499

497500
V1LikeTriggerRequest::Animation Parse(std::string_view value,
498501
USERVER_NAMESPACE::formats::parse::To<V1LikeTriggerRequest::Animation>);
Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,88 @@
11
#pragma once
22

3+
/// @file userver/chaotic/convert.hpp
4+
/// @brief @copybrief chaotic::ConvertTo
5+
6+
#include <cstdint>
7+
#include <string_view>
38
#include <type_traits>
49

510
#include <userver/chaotic/convert/to.hpp>
611

712
USERVER_NAMESPACE_BEGIN
813

9-
namespace chaotic::convert {
14+
namespace chaotic {
15+
namespace convert {
1016

11-
template <typename T, typename U>
12-
constexpr U Convert(const T& value, To<U>) {
17+
namespace impl {
18+
19+
template <typename YourType>
20+
constexpr void ReportMissingConvert(std::string_view, chaotic::convert::To<YourType>) {
21+
static_assert(
22+
sizeof(YourType) && false,
23+
"There is no `YourType Convert(std::string_view, chaotic::convert::To<YourType>)` in namespace of `YourType` "
24+
"or `chaotic::convert`. Probably you have not provided a `Convert` function overload."
25+
);
26+
}
27+
28+
template <typename YourType>
29+
constexpr void ReportMissingConvert(std::int64_t, chaotic::convert::To<YourType>) {
30+
static_assert(
31+
sizeof(YourType) && false,
32+
"There is no `YourType Convert(std::int64_t, chaotic::convert::To<YourType>)` in namespace of `YourType` or "
33+
"`chaotic::convert`. Probably you have not provided a `Convert` function overload."
34+
);
35+
}
36+
37+
template <class T, class YourType>
38+
constexpr void ReportMissingConvert(T&&, chaotic::convert::To<YourType>) {
1339
static_assert(
14-
std::is_constructible_v<U, const T&>,
15-
"There is no `Convert(const Value&, chaotic::convert::To<T>)` in "
16-
"namespace of `T` or `chaotic::convert`. Probably you have not provided "
17-
"a `Convert` function overload."
40+
sizeof(YourType) && false,
41+
"There is no `YourType Convert(T&&, chaotic::convert::To<YourType>)` in namespace of `T`, `YourType` or "
42+
"`chaotic::convert`. Probably you have not provided a `Convert` function overload."
1843
);
44+
}
45+
46+
template <typename YourType, typename T>
47+
constexpr void ReportMissingConvert(const YourType&, chaotic::convert::To<T>) {
48+
static_assert(
49+
sizeof(YourType) && false,
50+
"There is no `T Convert(const YourType&, chaotic::convert::To<T>)` in namespace of `T`, `YourType` or "
51+
"`chaotic::convert`. Probably you have not provided a `Convert` function overload."
52+
);
53+
}
54+
55+
template <typename TargetType>
56+
class ConvertCPO {
57+
public:
58+
template <typename... Args>
59+
constexpr void operator()(Args&&... args) const noexcept {
60+
static_assert(sizeof...(Args) == 1, "Should be used with a single argument only");
61+
impl::ReportMissingConvert(std::forward<Args>(args)..., chaotic::convert::To<TargetType>{});
62+
}
1963

64+
template <typename T>
65+
constexpr auto operator()(T&& value
66+
) const -> decltype(Convert(std::forward<T>(value), chaotic::convert::To<TargetType>{}))
67+
{
68+
return Convert(std::forward<T>(value), chaotic::convert::To<TargetType>{});
69+
}
70+
};
71+
72+
} // namespace impl
73+
74+
template <typename T, typename U>
75+
constexpr std::enable_if_t<std::is_constructible_v<U, const T&>, U> Convert(const T& value, To<U>) {
2076
return U{value};
2177
}
2278

23-
} // namespace chaotic::convert
79+
} // namespace convert
80+
81+
/// @brief Helper function to dispatch to user defined `Convert(X, To<Y>)` functions.
82+
/// Use like `chaotic::ConvertTo<TargetType>(value_from)`.
83+
template <typename To>
84+
constexpr inline convert::impl::ConvertCPO<To> ConvertTo{};
85+
86+
} // namespace chaotic
2487

2588
USERVER_NAMESPACE_END

chaotic/include/userver/chaotic/convert/to.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
#pragma once
22

3-
#include <string>
3+
/// @file userver/chaotic/convert/to.hpp
4+
/// @brief @copybrief chaotic::convert::to
45

56
USERVER_NAMESPACE_BEGIN
67

78
namespace chaotic::convert {
89

10+
/// @brief Helper type for defining `Convert` functions for chaotic.
11+
/// @see @ref convert::ConvertTo
912
template <typename T>
1013
struct To {};
1114

0 commit comments

Comments
 (0)