|
2 | 2 |
|
3 | 3 | /// @file userver/proto-structs/convert.hpp |
4 | 4 | /// @brief Functions for protobuf message to/from struct conversion |
| 5 | +/// |
| 6 | +/// Conversion functions rely on user-defined functions with the following signatures: |
| 7 | +/// 1. `StructType ReadProtoStruct(proto_structs::io::ReadContext&, |
| 8 | +/// proto_structs::io::To<StructType>, |
| 9 | +/// const MessageType&)` |
| 10 | +/// |
| 11 | +/// 2. `void WriteProtoStruct(proto_structs::io::WriteContext&, |
| 12 | +/// const StructType&, |
| 13 | +/// MessageType&)` |
| 14 | +/// |
| 15 | +/// This functions should be foundable by ADL lookup (just place them in the namespace where `StructType` is defined). |
| 16 | +/// |
| 17 | +/// It is recommended to define `WriteProtoStruct` function as template with a `StructType` parameter and use universal |
| 18 | +/// reference `StructType&&` as a second parameter to enable perfect forwarding for conversions. |
5 | 19 |
|
6 | 20 | #include <type_traits> |
7 | 21 | #include <utility> |
8 | 22 |
|
9 | | -#include <userver/proto-structs/exceptions.hpp> |
10 | | -#include <userver/proto-structs/impl/context.hpp> |
| 23 | +#include <userver/proto-structs/io/context.hpp> |
11 | 24 | #include <userver/proto-structs/type_mapping.hpp> |
12 | | -#include <userver/utils/assert.hpp> |
13 | 25 |
|
14 | 26 | USERVER_NAMESPACE_BEGIN |
15 | 27 |
|
16 | 28 | namespace proto_structs { |
17 | 29 |
|
18 | | -/// @brief Converts protobuf message @a msg to struct @a obj |
| 30 | +/// @brief Converts protobuf message @a msg to proto struct @a obj |
19 | 31 | /// @tparam TMessage protobuf message type |
20 | | -/// @tparam TStruct struct type |
21 | | -/// @throws ConversionError if conversion failed (in that case `obj` is not modified) |
22 | | -/// @note If function throws an exception, @a obj is not modified |
23 | | -/// |
24 | | -/// Function calls `ReadStruct(obj, msg)` under the hood which should be foundable by argument-dependent lookup. |
| 32 | +/// @tparam TStruct proto struct type |
| 33 | +/// @throws ReadError if conversion has failed |
| 34 | +/// @warning If function throws an exception, @a obj is left in a valid but unspecified state |
25 | 35 | template <traits::ProtoMessage TMessage, traits::ProtoStruct TStruct> |
26 | 36 | void MessageToStruct(const TMessage& msg, TStruct& obj) { |
27 | | - impl::ReadContext ctx; |
28 | | - obj = ReadStruct(ctx, To<std::remove_cv_t<TStruct>>{}, msg); |
29 | | - UINVARIANT(!ctx.HasError(), "Read context contains error that should have been thrown"); |
| 37 | + io::ReadContext ctx(*msg.GetDescriptor()); |
| 38 | + |
| 39 | + try { |
| 40 | + obj = ReadProtoStruct(ctx, io::To<std::remove_cv_t<TStruct>>{}, msg); |
| 41 | + } catch (const ReadError&) { |
| 42 | + // simply re-throw proper errors |
| 43 | + throw; |
| 44 | + } catch (const std::exception& e) { |
| 45 | + // some user-defined `ReadProtoStruct` threw an exception instead of adding an error to context, |
| 46 | + // adding it manually an re-throwing as a proper exception type |
| 47 | + ctx.AddError(e.what()); |
| 48 | + } |
30 | 49 | } |
31 | 50 |
|
32 | | -/// @brief Converts protobuf message @a msg to specified structure type |
33 | | -/// @tparam TStruct struct type |
| 51 | +/// @brief Converts protobuf message @a msg to specified proto struct type |
| 52 | +/// @tparam TStruct proto struct type |
34 | 53 | /// @tparam TMessage protobuf message type |
35 | | -/// @throws ConversionError if conversion failed |
36 | | -/// |
37 | | -/// Function calls `ReadStruct(obj, msg)` under the hood which should be foundable by argument-dependent lookup. |
| 54 | +/// @throws ReadError if conversion has failed |
38 | 55 | template <traits::ProtoStruct TStruct, traits::ProtoMessage TMessage> |
39 | 56 | [[nodiscard]] TStruct MessageToStruct(const TMessage& msg) { |
40 | 57 | TStruct obj; |
41 | 58 | MessageToStruct(msg, obj); |
42 | 59 | return obj; |
43 | 60 | } |
44 | 61 |
|
45 | | -/// @brief Converts struct instance @a obj to protobuf message @a msg |
46 | | -/// @tparam TStruct struct type |
| 62 | +/// @brief Converts proto struct @a obj to protobuf message @a msg |
| 63 | +/// @tparam TStruct proto struct type |
47 | 64 | /// @tparam TMessage protobuf message type |
48 | | -/// @throws ConversionError if conversion failed (in that case `msg` is not modified) |
| 65 | +/// @throws WriteError if conversion has failed |
49 | 66 | /// @warning If function throws an exception, @a msg is left in a valid but unspecified state |
50 | | -/// |
51 | | -/// Function calls `WriteStruct(std::forward<TStruct>(obj), msg)` under the hood which should be foundable by |
52 | | -/// argument-dependent lookup. |
53 | 67 | template <typename TStruct, traits::ProtoMessage TMessage> |
54 | 68 | requires traits::ProtoStruct<std::remove_cvref_t<TStruct>> |
55 | 69 | void StructToMessage(TStruct&& obj, TMessage& msg) { |
56 | | - impl::WriteContext ctx; |
57 | | - WriteStruct(ctx, std::forward<TStruct>(obj), msg); |
58 | | - UINVARIANT(!ctx.HasError(), "Write context contains error that should have been thrown"); |
59 | | -} |
| 70 | + io::WriteContext ctx(*msg.GetDescriptor()); |
60 | 71 |
|
61 | | -/// @brief Converts struct instance @a obj to protobuf message @a msg of the specified type |
62 | | -/// @tparam TStruct struct type |
63 | | -/// @tparam TMessage protobuf message type |
64 | | -/// @throws ConversionError if conversion failed |
65 | | -/// |
66 | | -/// Function calls `WriteStruct(std::forward<TStruct>(obj), msg)` under the hood which should be foundable by |
67 | | -/// argument-dependent lookup. |
68 | | -template <traits::ProtoMessage TMessage, typename TStruct> |
69 | | -requires traits::ProtoStruct<std::remove_cvref_t<TStruct>> |
70 | | -[[nodiscard]] TMessage StructToMessage(TStruct&& obj) { |
71 | | - TMessage msg; |
72 | | - StructToMessage(std::forward<TStruct>(obj), msg); |
73 | | - return msg; |
| 72 | + try { |
| 73 | + WriteProtoStruct(ctx, std::forward<TStruct>(obj), msg); |
| 74 | + } catch (const WriteError&) { |
| 75 | + // simply re-throw proper errors |
| 76 | + throw; |
| 77 | + } catch (const std::exception& e) { |
| 78 | + // some user-defined `WriteProtoStruct` threw an exception instead of adding an error to context, |
| 79 | + // adding it manually an re-throwing as a proper exception type |
| 80 | + ctx.AddError(e.what()); |
| 81 | + } |
74 | 82 | } |
75 | 83 |
|
76 | | -/// @brief Converts struct instance @a obj to it's compatible protobuf message |
77 | | -/// @tparam TStruct struct type |
78 | | -/// @throws ConversionError if conversion failed |
79 | | -/// |
80 | | -/// Compatability information should be provided for `TStruct` with @ref proto_structs::compatible_message, otherwise |
81 | | -/// compilation will fail. |
82 | | -/// |
83 | | -/// Function calls `WriteStruct(std::forward<TStruct>(obj), msg)` under the hood which should be foundable by |
84 | | -/// argument-dependent lookup. |
| 84 | +/// @brief Converts proto struct @a obj to it's compatible protobuf message type |
| 85 | +/// @tparam TStruct proto struct type |
| 86 | +/// @throws WriteError if conversion has failed |
85 | 87 | template <typename TStruct> |
86 | | -requires traits::CompatibleStruct<std::remove_cvref_t<TStruct>> |
| 88 | +requires traits::ProtoStruct<std::remove_cvref_t<TStruct>> |
87 | 89 | [[nodiscard]] traits::CompatibleMessageType<std::remove_cvref_t<TStruct>> StructToMessage(TStruct&& obj) { |
88 | 90 | using Message = traits::CompatibleMessageType<std::remove_cvref_t<TStruct>>; |
89 | | - return StructToMessage<Message>(std::forward<TStruct>(obj)); |
| 91 | + Message msg; |
| 92 | + StructToMessage(std::forward<TStruct>(obj), msg); |
| 93 | + return msg; |
90 | 94 | } |
91 | 95 |
|
92 | 96 | } // namespace proto_structs |
|
0 commit comments