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>
77struct ValueTraits<T, std::enable_if_t<meta::kIsRange<T> && !meta::kIsMap<T>>> {
78 using ValueType = meta::RangeValueType<T>;
79
80 static T Parse(NYdb::TValueParser& parser, const ParseContext& context) {
81 parser.OpenList();
82#if __cpp_lib_ranges_to_container >= 202202L
83 T result =
84 std::ranges::subrange{
85 impl::ParseItemsIterator<ValueType>{parser, context},
86 impl::ParseItemsIterator<ValueType>{},
87 } |
88 std::ranges::to<T>();
89#else
90 T result(impl::ParseItemsIterator<ValueType>{parser, context}, impl::ParseItemsIterator<ValueType>{});
91#endif
92 parser.CloseList();
93 return result;
94 }
95
96 template <typename Builder, typename U = const T&>
97 static void Write(NYdb::TValueBuilderBase<Builder>& builder, U&& value) {
98 bool list_opened = false;
99 for (auto&& item : value) {
100 if (!list_opened) {
101 builder.BeginList();
102 list_opened = true;
103 }
104 builder.AddListItem();
105 ydb::Write(builder, item);
106 }
107 if (list_opened) {
108 builder.EndList();
109 } else if constexpr (std::is_same_v<ValueType, InsertRow>) {
110 // Legacy behavior. Sometimes YDB will throw here due to insufficient type
111 // info.
112 builder.EmptyList();
113 } else {
114 builder.EmptyList(ValueTraits<ValueType>::MakeType());
115 }
116 }
117
118 static NYdb::TType MakeType() {
119 NYdb::TTypeBuilder builder;
120 builder.List(ValueTraits<ValueType>::MakeType());
121 return builder.Build();
122 }
123};
124
125template <typename T>
126struct ValueTraits<std::optional<T>, std::enable_if_t<meta::kIsRange<T> && !meta::kIsMap<T>>>
127 : impl::GenericOptionalValueTraits<T> {};
128
129} // namespace ydb
130
131USERVER_NAMESPACE_END