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