userver: userver/ydb/io/list.hpp Source File
Loading...
Searching...
No Matches
list.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/ydb/io/list.hpp
4/// @brief YDB list container serialization traits
5
6#include <ydb-cpp-sdk/client/value/value.h>
7
8#include <iterator>
9#include <ranges>
10#include <type_traits>
11#include <vector> // for InsertRow
12
13#include <userver/utils/assert.hpp>
14#include <userver/utils/meta.hpp>
15
16#include <userver/ydb/io/generic_optional.hpp>
17#include <userver/ydb/io/traits.hpp>
18
19USERVER_NAMESPACE_BEGIN
20
21namespace ydb {
22
23namespace impl {
24
25template <typename T>
26class ParseItemsIterator final {
27public:
28 using difference_type = std::ptrdiff_t;
29 using value_type = T;
30 using reference = T;
31 using iterator_category = std::input_iterator_tag;
32
33 ParseItemsIterator() = default;
34
35 ParseItemsIterator(NYdb::TValueParser& parser, const ParseContext& context)
36 : parser_(&parser),
37 context_(&context)
38 {
39 ++*this;
40 }
41
42 // Trivially copyable.
43 ParseItemsIterator(const ParseItemsIterator&) = default;
44 ParseItemsIterator& operator=(const ParseItemsIterator&) = default;
45
46 T operator*() const {
47 UASSERT(parser_ && context_);
48 return ydb::Parse<T>(*parser_, *context_);
49 }
50
51 ParseItemsIterator& operator++() {
52 UASSERT(parser_ && context_);
53 if (!parser_->TryNextListItem()) {
54 parser_ = nullptr;
55 }
56 return *this;
57 }
58
59 bool operator==(const ParseItemsIterator& other) const noexcept {
60 UASSERT(parser_ == nullptr || other.parser_ == nullptr);
61 return parser_ == other.parser_;
62 }
63
64private:
65 NYdb::TValueParser* parser_{nullptr};
66 const ParseContext* context_{nullptr};
67};
68
69} // namespace impl
70
71/// @cond
72struct InsertColumn;
73using InsertRow = std::vector<InsertColumn>;
74/// @endcond
75
76template <typename T>
77requires meta::kIsRange<T> && (!meta::kIsMap<T>)
78struct ValueTraits<T> {
79 using ValueType = meta::RangeValueType<T>;
80
81 static T Parse(NYdb::TValueParser& parser, const ParseContext& context) {
82 parser.OpenList();
83#if __cpp_lib_ranges_to_container >= 202202L
84 T result =
85 std::ranges::subrange{
86 impl::ParseItemsIterator<ValueType>{parser, context},
87 impl::ParseItemsIterator<ValueType>{},
88 } |
89 std::ranges::to<T>();
90#else
91 T result(impl::ParseItemsIterator<ValueType>{parser, context}, impl::ParseItemsIterator<ValueType>{});
92#endif
93 parser.CloseList();
94 return result;
95 }
96
97 template <typename Builder, typename U = const T&>
98 static void Write(NYdb::TValueBuilderBase<Builder>& builder, U&& value) {
99 bool list_opened = false;
100 for (auto&& item : value) {
101 if (!list_opened) {
102 builder.BeginList();
103 list_opened = true;
104 }
105 builder.AddListItem();
106 ydb::Write(builder, item);
107 }
108 if (list_opened) {
109 builder.EndList();
110 } else if constexpr (std::is_same_v<ValueType, InsertRow>) {
111 // Legacy behavior. Sometimes YDB will throw here due to insufficient type
112 // info.
113 builder.EmptyList();
114 } else {
115 builder.EmptyList(ValueTraits<ValueType>::MakeType());
116 }
117 }
118
119 static NYdb::TType MakeType() {
120 NYdb::TTypeBuilder builder;
121 builder.List(ValueTraits<ValueType>::MakeType());
122 return builder.Build();
123 }
124};
125
126template <typename T>
127requires meta::kIsRange<T> && (!meta::kIsMap<T>)
128struct ValueTraits<std::optional<T>> : impl::GenericOptionalValueTraits<T> {};
129
130} // namespace ydb
131
132USERVER_NAMESPACE_END