userver: userver/ydb/io/list.hpp Source File
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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) : parser_(&parser), context_(&context) {
33 ++*this;
34 }
35
36 // Trivially copyable.
37 ParseItemsIterator(const ParseItemsIterator&) = default;
38 ParseItemsIterator& operator=(const ParseItemsIterator&) = default;
39
40 T operator*() const {
41 UASSERT(parser_ && context_);
42 return ydb::Parse<T>(*parser_, *context_);
43 }
44
45 ParseItemsIterator& operator++() {
46 UASSERT(parser_ && context_);
47 if (!parser_->TryNextListItem()) {
48 parser_ = nullptr;
49 }
50 return *this;
51 }
52
53 bool operator==(const ParseItemsIterator& other) const noexcept {
54 UASSERT(parser_ == nullptr || other.parser_ == nullptr);
55 return parser_ == other.parser_;
56 }
57
58private:
59 NYdb::TValueParser* parser_{nullptr};
60 const ParseContext* context_{nullptr};
61};
62
63} // namespace impl
64
65/// @cond
66struct InsertColumn;
67using InsertRow = std::vector<InsertColumn>;
68/// @endcond
69
70template <typename T>
71struct ValueTraits<T, std::enable_if_t<meta::kIsRange<T> && !meta::kIsMap<T>>> {
72 using ValueType = meta::RangeValueType<T>;
73
74 static T Parse(NYdb::TValueParser& parser, const ParseContext& context) {
75 parser.OpenList();
76#if __cpp_lib_ranges_to_container >= 202202L
77 T result =
78 std::ranges::subrange{
79 impl::ParseItemsIterator<ValueType>{parser, context},
80 impl::ParseItemsIterator<ValueType>{},
81 } |
82 std::ranges::to<T>();
83#else
84 T result(impl::ParseItemsIterator<ValueType>{parser, context}, impl::ParseItemsIterator<ValueType>{});
85#endif
86 parser.CloseList();
87 return result;
88 }
89
90 template <typename Builder, typename U = const T&>
91 static void Write(NYdb::TValueBuilderBase<Builder>& builder, U&& value) {
92 bool list_opened = false;
93 for (auto&& item : value) {
94 if (!list_opened) {
95 builder.BeginList();
96 list_opened = true;
97 }
98 builder.AddListItem();
99 ydb::Write(builder, item);
100 }
101 if (list_opened) {
102 builder.EndList();
103 } else if constexpr (std::is_same_v<ValueType, InsertRow>) {
104 // Legacy behavior. Sometimes YDB will throw here due to insufficient type
105 // info.
106 builder.EmptyList();
107 } else {
108 builder.EmptyList(ValueTraits<ValueType>::MakeType());
109 }
110 }
111
112 static NYdb::TType MakeType() {
113 NYdb::TTypeBuilder builder;
114 builder.List(ValueTraits<ValueType>::MakeType());
115 return builder.Build();
116 }
117};
118
119template <typename T>
120struct ValueTraits<std::optional<T>, std::enable_if_t<meta::kIsRange<T> && !meta::kIsMap<T>>>
121 : impl::GenericOptionalValueTraits<T> {};
122
123} // namespace ydb
124
125USERVER_NAMESPACE_END