userver: userver/storages/redis/reply.hpp Source File
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
reply.hpp
1#pragma once
2
3#include <string>
4#include <variant>
5#include <vector>
6
7#include <userver/logging/log_extra.hpp>
8#include <userver/utils/assert.hpp>
9
10#include <userver/storages/redis/base.hpp>
11#include <userver/storages/redis/reply_fwd.hpp>
12#include <userver/storages/redis/reply_status.hpp>
13
14struct redisReply;
15
16USERVER_NAMESPACE_BEGIN
17
18namespace storages::redis {
19
20class ReplyData final {
21public:
22 using Array = std::vector<ReplyData>;
23
24 enum class Type {
25 kNoReply,
26 kString,
27 kArray,
28 kInteger,
29 kNil,
30 kStatus,
31 kError,
32 };
33
34 class MovableKeyValues final {
35 public:
36 class View final {
37 public:
38 View(ReplyData& key_data, ReplyData& value_data) noexcept : key_data_(key_data), value_data_(value_data) {}
39
40 std::string& Key() noexcept { return key_data_.GetString(); }
41 std::string& Value() noexcept { return value_data_.GetString(); }
42
43 private:
44 ReplyData& key_data_;
45 ReplyData& value_data_;
46 };
47
48 class Iterator final {
49 public:
50 constexpr Iterator(Array& array, std::size_t index) noexcept : array_(array), index_(index) {}
51 Iterator& operator++() noexcept {
52 ++index_;
53 return *this;
54 }
55 bool operator!=(const Iterator& r) const noexcept { return index_ != r.index_; }
56 View operator*() noexcept { return {array_[index_ * 2], array_[index_ * 2 + 1]}; }
57
58 private:
59 Array& array_;
60 std::size_t index_;
61 };
62
63 explicit MovableKeyValues(Array& array) noexcept : array_(array) {}
64
65 Iterator begin() const noexcept { return {array_, 0}; }
66 Iterator end() const noexcept { return {array_, size()}; }
67
68 std::size_t size() const noexcept { return array_.size() / 2; }
69
70 private:
71 Array& array_;
72 };
73
74 MovableKeyValues GetMovableKeyValues();
75
76 ReplyData(const redisReply* reply);
77 ReplyData(Array&& array);
78 ReplyData(std::string s);
79 ReplyData(int value);
80 static ReplyData CreateError(std::string&& error_msg);
81 static ReplyData CreateStatus(std::string&& status_msg);
82 static ReplyData CreateNil();
83
84 explicit operator bool() const noexcept { return GetType() != Type::kNoReply; }
85
86 Type GetType() const noexcept {
87 UASSERT(!data_.valueless_by_exception());
88 return Type(data_.index());
89 }
90 std::string GetTypeString() const;
91
92 inline bool IsString() const noexcept { return GetType() == Type::kString; }
93 inline bool IsArray() const noexcept { return GetType() == Type::kArray; }
94 inline bool IsInt() const noexcept { return GetType() == Type::kInteger; }
95 inline bool IsNil() const noexcept { return GetType() == Type::kNil; }
96 inline bool IsStatus() const noexcept { return GetType() == Type::kStatus; }
97 inline bool IsError() const noexcept { return GetType() == Type::kError; }
98 bool IsUnusableInstanceError() const;
99 bool IsReadonlyError() const;
100 bool IsUnknownCommandError() const;
101
102 bool IsErrorMoved() const { return IsError() && !GetError().compare(0, 6, "MOVED "); }
103
104 bool IsErrorAsk() const { return IsError() && !GetError().compare(0, 4, "ASK "); }
105
106 bool IsErrorClusterdown() const { return IsError() && !GetError().compare(0, 12, "CLUSTERDOWN "); }
107
108 const std::string& GetString() const {
109 UASSERT(IsString());
110 return std::get_if<String>(&data_)->GetUnderlying();
111 }
112
113 std::string& GetString() {
114 UASSERT(IsString());
115 return std::get_if<String>(&data_)->GetUnderlying();
116 }
117
118 const Array& GetArray() const {
119 UASSERT(IsArray());
120 return *std::get_if<Array>(&data_);
121 }
122
123 Array& GetArray() {
124 UASSERT(IsArray());
125 return *std::get_if<Array>(&data_);
126 }
127
128 std::int64_t GetInt() const {
129 UASSERT(IsInt());
130 return *std::get_if<Integer>(&data_);
131 }
132
133 const std::string& GetStatus() const {
134 UASSERT(IsStatus());
135 return std::get_if<Status>(&data_)->GetUnderlying();
136 }
137
138 std::string& GetStatus() {
139 UASSERT(IsStatus());
140 return std::get_if<Status>(&data_)->GetUnderlying();
141 }
142
143 const std::string& GetError() const {
144 UASSERT(IsError());
145 return std::get_if<Error>(&data_)->GetUnderlying();
146 }
147
148 std::string& GetError() {
149 UASSERT(IsError());
150 return std::get_if<Error>(&data_)->GetUnderlying();
151 }
152
153 std::size_t GetSize() const noexcept;
154
155 std::string ToDebugString() const;
156 static std::string TypeToString(Type type);
157
158 void ExpectType(ReplyData::Type type, const std::string& request_description = {}) const;
159
160 void ExpectString(const std::string& request_description = {}) const;
161 void ExpectArray(const std::string& request_description = {}) const;
162 void ExpectInt(const std::string& request_description = {}) const;
163 void ExpectNil(const std::string& request_description = {}) const;
164 void ExpectStatus(const std::string& request_description = {}) const;
165 void ExpectStatusEqualTo(const std::string& expected_status_str, const std::string& request_description = {}) const;
166 void ExpectError(const std::string& request_description = {}) const;
167
168private:
169 ReplyData() = default;
170
171 [[noreturn]] void ThrowUnexpectedReplyType(ReplyData::Type expected, const std::string& request_description) const;
172
173 struct NoReply {};
174 using String = utils::StrongTypedef<class StringTag, std::string>;
175 using Integer = std::int64_t;
176 struct Nil {};
177 using Status = utils::StrongTypedef<class StatusTag, std::string>;
178 using Error = utils::StrongTypedef<class ErrorTag, std::string>;
179
180 // Matches `enum class Type`
181 std::variant<NoReply, String, Array, Integer, Nil, Status, Error> data_{};
182};
183
184class Reply final {
185public:
186 Reply(std::string command, ReplyData&& reply_data, ReplyStatus reply_status = ReplyStatus::kOk);
187
188 std::string server;
189 ServerId server_id;
190 const std::string cmd;
191 ReplyData data;
192 const ReplyStatus status;
193 double time = 0.0;
194 logging::LogExtra log_extra;
195
196 operator bool() const { return IsOk(); }
197
198 std::string_view GetStatusString() const {
199 return (!IsOk() && data.IsError() ? data.GetError() : std::string_view{});
200 }
201
202 bool IsOk() const;
203 bool IsLoggableError() const;
204 bool IsUnusableInstanceError() const;
205 bool IsReadonlyError() const;
206 bool IsUnknownCommandError() const;
207 const logging::LogExtra& GetLogExtra() const;
208 void FillSpanTags(tracing::Span& span) const;
209
210 void ExpectIsOk(const std::string& request_description = {}) const;
211
212 const std::string& GetRequestDescription(const std::string& request_description) const;
213};
214
215} // namespace storages::redis
216
217USERVER_NAMESPACE_END